mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-12-29 12:37:40 +01:00
Change ContextSet 'satisfy' behaviour (#2300)
This commit is contained in:
parent
c09e4a1aa0
commit
ed85ab1bfd
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 net.luckperms.api.context;
|
||||
|
||||
import net.luckperms.api.query.OptionKey;
|
||||
|
||||
/**
|
||||
* Mode for determining whether a {@link ContextSet} satisfies another.
|
||||
*
|
||||
* @since 5.2
|
||||
* @see ContextSet#isSatisfiedBy(ContextSet, ContextSatisfyMode)
|
||||
*/
|
||||
public enum ContextSatisfyMode {
|
||||
|
||||
/**
|
||||
* Mode where a {@link ContextSet} A will be satisfied by another set B, if at least one of the
|
||||
* key-value entries per key in A are also in B.
|
||||
*
|
||||
* <p>For example, given <code>A = {server=survival, world=overworld, world=nether}</code>,
|
||||
* another set {@code X} will satisfy {@code A} if {@code X} contains
|
||||
* <code>server=survival AND (world=overworld OR world=nether)</code>.</p>
|
||||
*/
|
||||
AT_LEAST_ONE_VALUE_PER_KEY,
|
||||
|
||||
/**
|
||||
* Mode where a {@link ContextSet} A will be satisfied by another set B, if all key-value
|
||||
* entries in A are also in B.
|
||||
*
|
||||
* <p>For example, given <code>A = {server=survival, world=overworld, world=nether}</code>,
|
||||
* another set {@code X} will satisfy {@code A} if {@code X} contains
|
||||
* <code>server=survival AND world=overworld AND world=nether</code>.</p>
|
||||
*/
|
||||
ALL_VALUES_PER_KEY;
|
||||
|
||||
/**
|
||||
* The {@link OptionKey} for {@link ContextSatisfyMode}.
|
||||
*/
|
||||
public static final OptionKey<ContextSatisfyMode> KEY = OptionKey.of("contextsatisfymode", ContextSatisfyMode.class);
|
||||
|
||||
}
|
@ -197,17 +197,47 @@ public interface ContextSet extends Iterable<Context> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this {@link ContextSet} is fully "satisfied" by another set.
|
||||
* Returns if the {@link ContextSet} contains any of the given context pairings.
|
||||
*
|
||||
* <p>For a context set to "satisfy" another, it must itself contain all of
|
||||
* the context pairings in the other set.</p>
|
||||
*
|
||||
* <p>Mathematically, this method returns true if this set is a <b>subset</b> of the other.</p>
|
||||
*
|
||||
* @param other the other set to check
|
||||
* @return true if all entries in this set are also in the other set
|
||||
* @param key the key to look for
|
||||
* @param values the values to look for
|
||||
* @return true if the set contains any of the pairs
|
||||
* @since 5.2
|
||||
*/
|
||||
boolean isSatisfiedBy(@NonNull ContextSet other);
|
||||
default boolean containsAny(@NonNull String key, @NonNull Iterable<String> values) {
|
||||
Objects.requireNonNull(key, "key");
|
||||
Objects.requireNonNull(values, "values");
|
||||
|
||||
for (String value : values) {
|
||||
if (contains(key, value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this {@link ContextSet} is "satisfied" by another set.
|
||||
*
|
||||
* <p>{@link ContextSatisfyMode#AT_LEAST_ONE_VALUE_PER_KEY} is the mode used by this method.</p>
|
||||
*
|
||||
* @param other the other set
|
||||
* @return true if this context set is satisfied by the other
|
||||
*/
|
||||
default boolean isSatisfiedBy(@NonNull ContextSet other) {
|
||||
return isSatisfiedBy(other, ContextSatisfyMode.AT_LEAST_ONE_VALUE_PER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this {@link ContextSet} is "satisfied" by another set, according to the given
|
||||
* {@code mode}.
|
||||
*
|
||||
* @param other the other set
|
||||
* @param mode the mode to use
|
||||
* @return true if this context set is satisfied by the other
|
||||
* @since 5.2
|
||||
*/
|
||||
boolean isSatisfiedBy(@NonNull ContextSet other, @NonNull ContextSatisfyMode mode);
|
||||
|
||||
/**
|
||||
* Returns if the {@link ContextSet} is empty.
|
||||
|
@ -26,6 +26,7 @@
|
||||
package net.luckperms.api.query;
|
||||
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import net.luckperms.api.context.ContextSatisfyMode;
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
|
||||
@ -166,10 +167,29 @@ public interface QueryOptions {
|
||||
* Gets whether this {@link QueryOptions} satisfies the given required
|
||||
* {@link ContextSet context}.
|
||||
*
|
||||
* <p>{@link ContextSatisfyMode#AT_LEAST_ONE_VALUE_PER_KEY} is used if this {@link QueryOptions}
|
||||
* instance doesn't have the {@link ContextSatisfyMode#KEY option key} set.</p>
|
||||
*
|
||||
* @param contextSet the contexts
|
||||
* @return the result
|
||||
*/
|
||||
boolean satisfies(@NonNull ContextSet contextSet);
|
||||
default boolean satisfies(@NonNull ContextSet contextSet) {
|
||||
return satisfies(contextSet, ContextSatisfyMode.AT_LEAST_ONE_VALUE_PER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this {@link QueryOptions} satisfies the given required
|
||||
* {@link ContextSet context}.
|
||||
*
|
||||
* <p>The {@code defaultContextSatisfyMode} parameter is used if this {@link QueryOptions}
|
||||
* instance doesn't have the {@link ContextSatisfyMode#KEY option key} set.</p>
|
||||
*
|
||||
* @param contextSet the contexts
|
||||
* @param defaultContextSatisfyMode the default context satisfy mode to use
|
||||
* @return the result
|
||||
* @since 5.2
|
||||
*/
|
||||
boolean satisfies(@NonNull ContextSet contextSet, @NonNull ContextSatisfyMode defaultContextSatisfyMode);
|
||||
|
||||
/**
|
||||
* Converts this {@link QueryOptions} to a mutable builder.
|
||||
|
@ -46,11 +46,11 @@ import me.lucko.luckperms.common.util.Uuids;
|
||||
import me.lucko.luckperms.common.verbose.event.MetaCheckEvent;
|
||||
import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent;
|
||||
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.context.MutableContextSet;
|
||||
import net.luckperms.api.model.data.DataType;
|
||||
import net.luckperms.api.node.Node;
|
||||
import net.luckperms.api.node.NodeType;
|
||||
import net.luckperms.api.query.Flag;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
@ -251,10 +251,9 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission {
|
||||
Objects.requireNonNull(uuid, "uuid");
|
||||
|
||||
PermissionHolder user = lookupUser(uuid);
|
||||
ContextSet contexts = getQueryOptions(uuid, world).context();
|
||||
QueryOptions queryOptions = getQueryOptions(uuid, world);
|
||||
|
||||
return user.normalData().immutableInheritance().values().stream()
|
||||
.filter(n -> n.getContexts().isSatisfiedBy(contexts))
|
||||
return user.getOwnNodes(NodeType.INHERITANCE, queryOptions).stream()
|
||||
.map(n -> {
|
||||
Group group = this.plugin.getGroupManager().getIfLoaded(n.getGroupName());
|
||||
if (group != null) {
|
||||
|
@ -400,6 +400,15 @@ inheritance-traversal-algorithm: depth-first-pre-order
|
||||
# and when this setting is 'false':, the rules are just applied during each step of the traversal.
|
||||
post-traversal-inheritance-sort: false
|
||||
|
||||
# Defines the mode used to determine whether a set of contexts are satisfied.
|
||||
#
|
||||
# - Possible options:
|
||||
# => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the
|
||||
# key-value entries per key in A are also in B.
|
||||
# => all-values-per-key Set A will be satisfied by another set B, if all key-value
|
||||
# entries in A are also in B.
|
||||
context-satisfy-mode: at-least-one-value-per-key
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | Permission resolution settings | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
|
@ -408,6 +408,15 @@ inheritance-traversal-algorithm: depth-first-pre-order
|
||||
# and when this setting is 'false':, the rules are just applied during each step of the traversal.
|
||||
post-traversal-inheritance-sort: false
|
||||
|
||||
# Defines the mode used to determine whether a set of contexts are satisfied.
|
||||
#
|
||||
# - Possible options:
|
||||
# => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the
|
||||
# key-value entries per key in A are also in B.
|
||||
# => all-values-per-key Set A will be satisfied by another set B, if all key-value
|
||||
# entries in A are also in B.
|
||||
context-satisfy-mode: at-least-one-value-per-key
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | Permission resolution settings | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
|
@ -41,6 +41,7 @@ import me.lucko.luckperms.common.storage.implementation.split.SplitStorageType;
|
||||
import me.lucko.luckperms.common.storage.misc.StorageCredentials;
|
||||
import me.lucko.luckperms.common.util.ImmutableCollectors;
|
||||
|
||||
import net.luckperms.api.context.ContextSatisfyMode;
|
||||
import net.luckperms.api.metastacking.DuplicateRemovalFunction;
|
||||
import net.luckperms.api.metastacking.MetaStackDefinition;
|
||||
import net.luckperms.api.model.data.TemporaryNodeMergeStrategy;
|
||||
@ -114,6 +115,17 @@ public final class ConfigKeys {
|
||||
return new QueryOptionsBuilderImpl(QueryMode.CONTEXTUAL).flags(flags).build();
|
||||
});
|
||||
|
||||
/**
|
||||
* The default contexts satisfy mode
|
||||
*/
|
||||
public static final ConfigKey<ContextSatisfyMode> CONTEXT_SATISFY_MODE = customKey(c -> {
|
||||
String value = c.getString("context-satisfy-mode", "at-least-one-value-per-key");
|
||||
if (value.toLowerCase().equals("all-values-per-key")) {
|
||||
return ContextSatisfyMode.ALL_VALUES_PER_KEY;
|
||||
}
|
||||
return ContextSatisfyMode.AT_LEAST_ONE_VALUE_PER_KEY;
|
||||
});
|
||||
|
||||
/**
|
||||
* # If the servers own UUID cache/lookup facility should be used when there is no record for a player in the LuckPerms cache.
|
||||
*/
|
||||
|
@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
|
||||
import net.luckperms.api.context.Context;
|
||||
import net.luckperms.api.context.ContextSatisfyMode;
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.DefaultContextKeys;
|
||||
|
||||
@ -60,6 +61,36 @@ public abstract class AbstractContextSet implements ContextSet {
|
||||
return backing().containsEntry(sanitizeKey(key), sanitizeValue(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfiedBy(@NonNull ContextSet other, @NonNull ContextSatisfyMode mode) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Objects.requireNonNull(other, "other");
|
||||
Objects.requireNonNull(mode, "mode");
|
||||
|
||||
// this is empty, it is always satisfied.
|
||||
if (this.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if this set isn't empty, but the other one is, then it can't be satisfied by it.
|
||||
if (other.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if mode is ALL_VALUES & this set has more entries than the other one, then it can't be satisfied by it.
|
||||
if (mode == ContextSatisfyMode.ALL_VALUES_PER_KEY && this.size() > other.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if 'other' contains all of 'this', according to the mode.
|
||||
return otherContainsAll(other, mode);
|
||||
}
|
||||
|
||||
protected abstract boolean otherContainsAll(ContextSet other, ContextSatisfyMode mode);
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return backing().isEmpty();
|
||||
|
@ -34,12 +34,14 @@ import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
|
||||
import net.luckperms.api.context.Context;
|
||||
import net.luckperms.api.context.ContextSatisfyMode;
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.luckperms.api.context.MutableContextSet;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -133,30 +135,31 @@ public final class ImmutableContextSetImpl extends AbstractContextSet implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfiedBy(@NonNull ContextSet other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Objects.requireNonNull(other, "other");
|
||||
if (this.isEmpty()) {
|
||||
// this is empty, so is therefore always satisfied.
|
||||
return true;
|
||||
} else if (other.isEmpty()) {
|
||||
// this set isn't empty, but the other one is
|
||||
return false;
|
||||
} else if (this.size() > other.size()) {
|
||||
// this set has more unique entries than the other set, so there's no way this can be satisfied.
|
||||
return false;
|
||||
} else {
|
||||
// neither are empty, we need to compare the individual entries
|
||||
Set<Map.Entry<String, String>> entries = this.map.entries();
|
||||
for (Map.Entry<String, String> e : entries) {
|
||||
if (!other.contains(e.getKey(), e.getValue())) {
|
||||
return false;
|
||||
protected boolean otherContainsAll(ContextSet other, ContextSatisfyMode mode) {
|
||||
switch (mode) {
|
||||
// Use other.contains
|
||||
case ALL_VALUES_PER_KEY: {
|
||||
Set<Map.Entry<String, String>> entries = this.map.entries();
|
||||
for (Map.Entry<String, String> e : entries) {
|
||||
if (!other.contains(e.getKey(), e.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
// Use other.containsAny
|
||||
case AT_LEAST_ONE_VALUE_PER_KEY: {
|
||||
Set<Map.Entry<String, Collection<String>>> entries = this.map.asMap().entrySet();
|
||||
for (Map.Entry<String, Collection<String>> e : entries) {
|
||||
if (!other.containsAny(e.getKey(), e.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown mode: " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
|
||||
import net.luckperms.api.context.Context;
|
||||
import net.luckperms.api.context.ContextSatisfyMode;
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.luckperms.api.context.MutableContextSet;
|
||||
@ -186,32 +187,35 @@ public final class MutableContextSetImpl extends AbstractContextSet implements M
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfiedBy(@NonNull ContextSet other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Objects.requireNonNull(other, "other");
|
||||
if (this.isEmpty()) {
|
||||
// this is empty, so is therefore always satisfied.
|
||||
return true;
|
||||
} else if (other.isEmpty()) {
|
||||
// this set isn't empty, but the other one is
|
||||
return false;
|
||||
} else if (this.size() > other.size()) {
|
||||
// this set has more unique entries than the other set, so there's no way this can be satisfied.
|
||||
return false;
|
||||
} else {
|
||||
// neither are empty, we need to compare the individual entries
|
||||
Set<Map.Entry<String, String>> entries = this.map.entries();
|
||||
synchronized (this.map) {
|
||||
for (Map.Entry<String, String> e : entries) {
|
||||
if (!other.contains(e.getKey(), e.getValue())) {
|
||||
return false;
|
||||
protected boolean otherContainsAll(ContextSet other, ContextSatisfyMode mode) {
|
||||
switch (mode) {
|
||||
// Use other.contains
|
||||
case ALL_VALUES_PER_KEY: {
|
||||
Set<Map.Entry<String, String>> entries = this.map.entries();
|
||||
synchronized (this.map) {
|
||||
for (Map.Entry<String, String> e : entries) {
|
||||
if (!other.contains(e.getKey(), e.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
// Use other.containsAny
|
||||
case AT_LEAST_ONE_VALUE_PER_KEY: {
|
||||
Set<Map.Entry<String, Collection<String>>> entries = this.map.asMap().entrySet();
|
||||
synchronized (this.map) {
|
||||
for (Map.Entry<String, Collection<String>> e : entries) {
|
||||
if (!other.containsAny(e.getKey(), e.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown mode: " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,11 +31,13 @@ import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import me.lucko.luckperms.common.cache.Cache;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.context.ContextSetComparator;
|
||||
import me.lucko.luckperms.common.node.comparator.NodeComparator;
|
||||
import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator;
|
||||
import me.lucko.luckperms.common.query.QueryOptionsImpl;
|
||||
|
||||
import net.luckperms.api.context.ContextSatisfyMode;
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
@ -154,9 +156,13 @@ public final class NodeMap {
|
||||
!flagExcludeTest(Flag.APPLY_INHERITANCE_NODES_WITHOUT_WORLD_CONTEXT, DefaultContextKeys.WORLD_KEY, filter, contextSet);
|
||||
}
|
||||
|
||||
private ContextSatisfyMode defaultSatisfyMode() {
|
||||
return this.holder.getPlugin().getConfiguration().get(ConfigKeys.CONTEXT_SATISFY_MODE);
|
||||
}
|
||||
|
||||
public void forEach(QueryOptions filter, Consumer<? super Node> consumer) {
|
||||
for (Map.Entry<ImmutableContextSet, SortedSet<Node>> e : this.map.entrySet()) {
|
||||
if (!filter.satisfies(e.getKey())) {
|
||||
if (!filter.satisfies(e.getKey(), defaultSatisfyMode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -176,7 +182,7 @@ public final class NodeMap {
|
||||
|
||||
public void copyTo(Collection<? super Node> collection, QueryOptions filter) {
|
||||
for (Map.Entry<ImmutableContextSet, SortedSet<Node>> e : this.map.entrySet()) {
|
||||
if (!filter.satisfies(e.getKey())) {
|
||||
if (!filter.satisfies(e.getKey(), defaultSatisfyMode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -196,7 +202,7 @@ public final class NodeMap {
|
||||
|
||||
public <T extends Node> void copyTo(Collection<? super T> collection, NodeType<T> type, QueryOptions filter) {
|
||||
for (Map.Entry<ImmutableContextSet, SortedSet<Node>> e : this.map.entrySet()) {
|
||||
if (!filter.satisfies(e.getKey())) {
|
||||
if (!filter.satisfies(e.getKey(), defaultSatisfyMode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -224,7 +230,7 @@ public final class NodeMap {
|
||||
|
||||
public void copyInheritanceNodesTo(Collection<? super InheritanceNode> collection, QueryOptions filter) {
|
||||
for (Map.Entry<ImmutableContextSet, SortedSet<InheritanceNode>> e : this.inheritanceMap.entrySet()) {
|
||||
if (!filter.satisfies(e.getKey())) {
|
||||
if (!filter.satisfies(e.getKey(), defaultSatisfyMode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
|
||||
|
||||
import net.luckperms.api.context.ContextSatisfyMode;
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.luckperms.api.query.Flag;
|
||||
@ -49,20 +50,25 @@ public class QueryOptionsImpl implements QueryOptions {
|
||||
public static final QueryOptions DEFAULT_CONTEXTUAL = new QueryOptionsImpl(QueryMode.CONTEXTUAL, ImmutableContextSetImpl.EMPTY, FlagUtils.DEFAULT_FLAGS, null);
|
||||
public static final QueryOptions DEFAULT_NON_CONTEXTUAL = new QueryOptionsImpl(QueryMode.NON_CONTEXTUAL, null, FlagUtils.DEFAULT_FLAGS, null);
|
||||
|
||||
// state
|
||||
private final QueryMode mode;
|
||||
private final ImmutableContextSet context;
|
||||
private final byte flags;
|
||||
private final ImmutableMap<OptionKey<?>, Object> options;
|
||||
private final int hashCode;
|
||||
|
||||
// computed based on state above
|
||||
private final int hashCode;
|
||||
private Set<Flag> flagsSet = null;
|
||||
private final ContextSatisfyMode contextSatisfyMode;
|
||||
|
||||
QueryOptionsImpl(QueryMode mode, @Nullable ImmutableContextSet context, byte flags, @Nullable Map<OptionKey<?>, Object> options) {
|
||||
this.mode = mode;
|
||||
this.context = context;
|
||||
this.flags = flags;
|
||||
this.options = options == null ? null : ImmutableMap.copyOf(options);
|
||||
|
||||
this.hashCode = calculateHashCode();
|
||||
this.contextSatisfyMode = options == null ? null : (ContextSatisfyMode) options.get(ContextSatisfyMode.KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -113,10 +119,10 @@ public class QueryOptionsImpl implements QueryOptions {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean satisfies(@NonNull ContextSet contextSet) {
|
||||
public boolean satisfies(@NonNull ContextSet contextSet, @NonNull ContextSatisfyMode defaultContextSatisfyMode) {
|
||||
switch (this.mode) {
|
||||
case CONTEXTUAL:
|
||||
return contextSet.isSatisfiedBy(this.context);
|
||||
return contextSet.isSatisfiedBy(this.context, this.contextSatisfyMode == null ? defaultContextSatisfyMode : this.contextSatisfyMode);
|
||||
case NON_CONTEXTUAL:
|
||||
return true;
|
||||
default:
|
||||
|
@ -395,6 +395,15 @@ inheritance-traversal-algorithm: depth-first-pre-order
|
||||
# and when this setting is 'false':, the rules are just applied during each step of the traversal.
|
||||
post-traversal-inheritance-sort: false
|
||||
|
||||
# Defines the mode used to determine whether a set of contexts are satisfied.
|
||||
#
|
||||
# - Possible options:
|
||||
# => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the
|
||||
# key-value entries per key in A are also in B.
|
||||
# => all-values-per-key Set A will be satisfied by another set B, if all key-value
|
||||
# entries in A are also in B.
|
||||
context-satisfy-mode: at-least-one-value-per-key
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | Permission resolution settings | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
|
@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.context.ContextSetComparator;
|
||||
import me.lucko.luckperms.sponge.service.ProxyFactory;
|
||||
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
||||
@ -36,6 +37,7 @@ import me.lucko.luckperms.sponge.service.model.LPSubject;
|
||||
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
|
||||
import me.lucko.luckperms.sponge.service.model.LPSubjectReference;
|
||||
|
||||
import net.luckperms.api.context.ContextSatisfyMode;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.luckperms.api.model.data.DataType;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
@ -87,6 +89,10 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
private ContextSatisfyMode defaultSatisfyMode() {
|
||||
return this.service.getPlugin().getConfiguration().get(ConfigKeys.CONTEXT_SATISFY_MODE);
|
||||
}
|
||||
|
||||
public void replacePermissions(Map<ImmutableContextSet, Map<String, Boolean>> map) {
|
||||
this.permissions.clear();
|
||||
for (Map.Entry<ImmutableContextSet, Map<String, Boolean>> e : map.entrySet()) {
|
||||
@ -131,7 +137,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
// get relevant entries
|
||||
SortedMap<ImmutableContextSet, Map<String, Boolean>> sorted = new TreeMap<>(ContextSetComparator.reverse());
|
||||
for (Map.Entry<ImmutableContextSet, Map<String, Boolean>> entry : this.permissions.entrySet()) {
|
||||
if (!filter.satisfies(entry.getKey())) {
|
||||
if (!filter.satisfies(entry.getKey(), defaultSatisfyMode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -209,7 +215,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
// get relevant entries
|
||||
SortedMap<ImmutableContextSet, Set<LPSubjectReference>> sorted = new TreeMap<>(ContextSetComparator.reverse());
|
||||
for (Map.Entry<ImmutableContextSet, Set<LPSubjectReference>> entry : this.parents.entrySet()) {
|
||||
if (!filter.satisfies(entry.getKey())) {
|
||||
if (!filter.satisfies(entry.getKey(), defaultSatisfyMode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -285,7 +291,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
// get relevant entries
|
||||
SortedMap<ImmutableContextSet, Map<String, String>> sorted = new TreeMap<>(ContextSetComparator.reverse());
|
||||
for (Map.Entry<ImmutableContextSet, Map<String, String>> entry : this.options.entrySet()) {
|
||||
if (!filter.satisfies(entry.getKey())) {
|
||||
if (!filter.satisfies(entry.getKey(), defaultSatisfyMode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -409,6 +409,15 @@ inheritance-traversal-algorithm = "depth-first-pre-order"
|
||||
# and when this setting is 'false':, the rules are just applied during each step of the traversal.
|
||||
post-traversal-inheritance-sort = false
|
||||
|
||||
# Defines the mode used to determine whether a set of contexts are satisfied.
|
||||
#
|
||||
# - Possible options:
|
||||
# => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the
|
||||
# key-value entries per key in A are also in B.
|
||||
# => all-values-per-key Set A will be satisfied by another set B, if all key-value
|
||||
# entries in A are also in B.
|
||||
context-satisfy-mode = "at-least-one-value-per-key"
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | Permission resolution settings | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
|
@ -399,6 +399,15 @@ inheritance-traversal-algorithm: depth-first-pre-order
|
||||
# and when this setting is 'false':, the rules are just applied during each step of the traversal.
|
||||
post-traversal-inheritance-sort: false
|
||||
|
||||
# Defines the mode used to determine whether a set of contexts are satisfied.
|
||||
#
|
||||
# - Possible options:
|
||||
# => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the
|
||||
# key-value entries per key in A are also in B.
|
||||
# => all-values-per-key Set A will be satisfied by another set B, if all key-value
|
||||
# entries in A are also in B.
|
||||
context-satisfy-mode: at-least-one-value-per-key
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | Permission resolution settings | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
|
Loading…
Reference in New Issue
Block a user