mirror of
https://github.com/lucko/LuckPerms.git
synced 2025-09-24 21:11:41 +02:00
Abstract out the process of traversing the inheritance tree, add configurable option to choose which algorithm to use (#719)
This commit is contained in:
@@ -63,6 +63,7 @@ import me.lucko.luckperms.common.dependencies.DependencyRegistry;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.ReflectionClassLoader;
|
||||
import me.lucko.luckperms.common.event.EventFactory;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceHandler;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.NoopLocaleManager;
|
||||
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
|
||||
@@ -131,6 +132,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
private LocaleManager localeManager;
|
||||
private PluginClassLoader pluginClassLoader;
|
||||
private DependencyManager dependencyManager;
|
||||
private InheritanceHandler inheritanceHandler;
|
||||
private CachedStateManager cachedStateManager;
|
||||
private ContextManager<Player> contextManager;
|
||||
private CalculatorFactory calculatorFactory;
|
||||
@@ -233,6 +235,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
|
||||
// load internal managers
|
||||
getLog().info("Loading internal permission managers...");
|
||||
this.inheritanceHandler = new InheritanceHandler(this);
|
||||
this.userManager = new StandardUserManager(this);
|
||||
this.groupManager = new StandardGroupManager(this);
|
||||
this.trackManager = new StandardTrackManager(this);
|
||||
@@ -673,6 +676,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
return this.contextManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InheritanceHandler getInheritanceHandler() {
|
||||
return this.inheritanceHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatorFactory getCalculatorFactory() {
|
||||
return this.calculatorFactory;
|
||||
|
@@ -279,7 +279,7 @@ public class VaultChatHook extends AbstractVaultChat {
|
||||
}
|
||||
|
||||
// find the max inherited priority & add 10
|
||||
MetaAccumulator metaAccumulator = holder.accumulateMeta(null, null, createContextForWorldSet(world));
|
||||
MetaAccumulator metaAccumulator = holder.accumulateMeta(null, createContextForWorldSet(world));
|
||||
int priority = (type == ChatMetaType.PREFIX ? metaAccumulator.getPrefixes() : metaAccumulator.getSuffixes()).keySet().stream()
|
||||
.mapToInt(e -> e).max().orElse(0) + 10;
|
||||
|
||||
|
@@ -134,6 +134,18 @@ apply-bukkit-default-permissions: true
|
||||
# permissions when considering if a player should have access to a certain permission.
|
||||
apply-bukkit-attachment-permissions: true
|
||||
|
||||
# The algorithm LuckPerms should use when traversing the "inheritance tree".
|
||||
#
|
||||
# The valid options are:
|
||||
# - breadth-first
|
||||
# - depth-first-pre-order
|
||||
# - depth-first-post-order
|
||||
#
|
||||
# See here for information about the differences between each algorithm.
|
||||
# - https://en.wikipedia.org/wiki/Breadth-first_search
|
||||
# - https://en.wikipedia.org/wiki/Depth-first_search
|
||||
inheritance-traversal-algorithm: depth-first-pre-order
|
||||
|
||||
# Define special group weights for this server.
|
||||
# Default is just 0.
|
||||
group-weight:
|
||||
|
@@ -54,6 +54,7 @@ import me.lucko.luckperms.common.dependencies.DependencyRegistry;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.ReflectionClassLoader;
|
||||
import me.lucko.luckperms.common.event.EventFactory;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceHandler;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.NoopLocaleManager;
|
||||
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
|
||||
@@ -112,6 +113,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
||||
private LocaleManager localeManager;
|
||||
private PluginClassLoader pluginClassLoader;
|
||||
private DependencyManager dependencyManager;
|
||||
private InheritanceHandler inheritanceHandler;
|
||||
private CachedStateManager cachedStateManager;
|
||||
private ContextManager<ProxiedPlayer> contextManager;
|
||||
private CalculatorFactory calculatorFactory;
|
||||
@@ -182,6 +184,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
||||
|
||||
// load internal managers
|
||||
getLog().info("Loading internal permission managers...");
|
||||
this.inheritanceHandler = new InheritanceHandler(this);
|
||||
this.userManager = new StandardUserManager(this);
|
||||
this.groupManager = new StandardGroupManager(this);
|
||||
this.trackManager = new StandardTrackManager(this);
|
||||
@@ -460,6 +463,11 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
||||
return this.contextManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InheritanceHandler getInheritanceHandler() {
|
||||
return this.inheritanceHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatorFactory getCalculatorFactory() {
|
||||
return this.calculatorFactory;
|
||||
|
@@ -27,35 +27,35 @@ package me.lucko.luckperms.bungee.event;
|
||||
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Copy of the internal BungeeCord PermissionCheckEvent, returning a tristate instead of a boolean.
|
||||
*/
|
||||
public class TristateCheckEvent extends Event {
|
||||
public static Tristate call(ProxiedPlayer player, String permission) {
|
||||
return ProxyServer.getInstance().getPluginManager().callEvent(new TristateCheckEvent(player, permission)).getResult();
|
||||
public static Tristate call(CommandSender sender, String permission) {
|
||||
return ProxyServer.getInstance().getPluginManager().callEvent(new TristateCheckEvent(sender, permission)).getResult();
|
||||
}
|
||||
|
||||
private final ProxiedPlayer player;
|
||||
private final CommandSender sender;
|
||||
private final String permission;
|
||||
|
||||
private Tristate result;
|
||||
|
||||
public TristateCheckEvent(ProxiedPlayer player, String permission) {
|
||||
this(player, permission, player.getPermissions().contains(permission) ? Tristate.TRUE : Tristate.UNDEFINED);
|
||||
public TristateCheckEvent(CommandSender sender, String permission) {
|
||||
this(sender, permission, sender.getPermissions().contains(permission) ? Tristate.TRUE : Tristate.UNDEFINED);
|
||||
}
|
||||
|
||||
public TristateCheckEvent(ProxiedPlayer player, String permission, Tristate result) {
|
||||
this.player = player;
|
||||
public TristateCheckEvent(CommandSender sender, String permission, Tristate result) {
|
||||
this.sender = sender;
|
||||
this.permission = permission;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public ProxiedPlayer getPlayer() {
|
||||
return this.player;
|
||||
public CommandSender getSender() {
|
||||
return this.sender;
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
@@ -73,7 +73,7 @@ public class TristateCheckEvent extends Event {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TristateCheckEvent(" +
|
||||
"player=" + this.player + ", " +
|
||||
"sender=" + this.sender + ", " +
|
||||
"permission=" + this.permission + ", " +
|
||||
"result=" + this.result + ")";
|
||||
}
|
||||
|
@@ -77,10 +77,14 @@ public class BungeePermissionCheckListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerTristateCheck(TristateCheckEvent e) {
|
||||
ProxiedPlayer player = e.getPlayer();
|
||||
if (!(e.getSender() instanceof ProxiedPlayer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Objects.requireNonNull(e.getPermission(), "permission");
|
||||
Objects.requireNonNull(player, "player");
|
||||
Objects.requireNonNull(e.getSender(), "sender");
|
||||
|
||||
ProxiedPlayer player = ((ProxiedPlayer) e.getSender());
|
||||
|
||||
User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
|
||||
if (user == null) {
|
||||
|
@@ -131,6 +131,18 @@ apply-shorthand: true
|
||||
# If set to false, LuckPerms will ignore these values.
|
||||
apply-bungee-config-permissions: false
|
||||
|
||||
# The algorithm LuckPerms should use when traversing the "inheritance tree".
|
||||
#
|
||||
# The valid options are:
|
||||
# - breadth-first
|
||||
# - depth-first-pre-order
|
||||
# - depth-first-post-order
|
||||
#
|
||||
# See here for information about the differences between each algorithm.
|
||||
# - https://en.wikipedia.org/wiki/Breadth-first_search
|
||||
# - https://en.wikipedia.org/wiki/Depth-first_search
|
||||
inheritance-traversal-algorithm: depth-first-pre-order
|
||||
|
||||
# Define special group weights for this server.
|
||||
# Default is just 0.
|
||||
group-weight:
|
||||
|
@@ -117,9 +117,9 @@ public abstract class HolderCachedData<T extends PermissionHolder> implements Ca
|
||||
}
|
||||
|
||||
if (contexts.getContexts() == Contexts.allowAll()) {
|
||||
data.loadMeta(this.holder.accumulateMeta(newAccumulator(contexts), null));
|
||||
data.loadMeta(this.holder.accumulateMeta(newAccumulator(contexts)));
|
||||
} else {
|
||||
data.loadMeta(this.holder.accumulateMeta(newAccumulator(contexts), null, contexts.getContexts()));
|
||||
data.loadMeta(this.holder.accumulateMeta(newAccumulator(contexts), contexts.getContexts()));
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@@ -37,6 +37,7 @@ import me.lucko.luckperms.common.config.keys.IntegerKey;
|
||||
import me.lucko.luckperms.common.config.keys.LowercaseStringKey;
|
||||
import me.lucko.luckperms.common.config.keys.MapKey;
|
||||
import me.lucko.luckperms.common.config.keys.StringKey;
|
||||
import me.lucko.luckperms.common.inheritance.graph.TraversalAlgorithm;
|
||||
import me.lucko.luckperms.common.metastacking.SimpleMetaStackDefinition;
|
||||
import me.lucko.luckperms.common.metastacking.StandardStackElements;
|
||||
import me.lucko.luckperms.common.model.TemporaryModifier;
|
||||
@@ -222,6 +223,21 @@ public class ConfigKeys {
|
||||
*/
|
||||
public static final ConfigKey<Boolean> APPLY_SPONGE_DEFAULT_SUBJECTS = EnduringKey.wrap(BooleanKey.of("apply-sponge-default-subjects", true));
|
||||
|
||||
/**
|
||||
* The algorithm LuckPerms should use when traversing the "inheritance tree"
|
||||
*/
|
||||
public static final ConfigKey<TraversalAlgorithm> INHERITANCE_TRAVERSAL_ALGORITHM = AbstractKey.of(c -> {
|
||||
String value = c.getString("inheritance-traversal-algorithm", "depth-first-pre-order");
|
||||
switch (value.toLowerCase()) {
|
||||
case "breadth-first":
|
||||
return TraversalAlgorithm.BREADTH_FIRST;
|
||||
case "depth-first-post-order":
|
||||
return TraversalAlgorithm.DEPTH_FIRST_POST_ORDER;
|
||||
default:
|
||||
return TraversalAlgorithm.DEPTH_FIRST_PRE_ORDER;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The configured group weightings
|
||||
*/
|
||||
|
@@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.primarygroup;
|
||||
package me.lucko.luckperms.common.inheritance;
|
||||
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
@@ -35,19 +35,19 @@ import java.util.Comparator;
|
||||
/**
|
||||
* Determines the order of group inheritance in {@link PermissionHolder}.
|
||||
*/
|
||||
public class GroupInheritanceComparator implements Comparator<Group> {
|
||||
private static final Comparator<Group> NULL_ORIGIN = new GroupInheritanceComparator(null);
|
||||
public class InheritanceComparator implements Comparator<Group> {
|
||||
private static final Comparator<Group> NULL_ORIGIN = new InheritanceComparator(null);
|
||||
|
||||
public static Comparator<Group> getFor(PermissionHolder origin) {
|
||||
if (origin.getType().isUser()) {
|
||||
return new GroupInheritanceComparator(((User) origin));
|
||||
return new InheritanceComparator(((User) origin));
|
||||
}
|
||||
return NULL_ORIGIN;
|
||||
}
|
||||
|
||||
private final User origin;
|
||||
|
||||
private GroupInheritanceComparator(User origin) {
|
||||
private InheritanceComparator(User origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.inheritance;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.common.inheritance.graph.Graph;
|
||||
import me.lucko.luckperms.common.inheritance.graph.GraphTraversers;
|
||||
import me.lucko.luckperms.common.inheritance.graph.TraversalAlgorithm;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* A {@link Graph} which represents an "inheritance tree".
|
||||
*/
|
||||
public interface InheritanceGraph extends Graph<PermissionHolder> {
|
||||
|
||||
/**
|
||||
* Returns an iterable which will traverse this inheritance graph using the
|
||||
* specified algorithm starting at the given node.
|
||||
*
|
||||
* @param algorithm the algorithm to use when traversing
|
||||
* @param startNode the start node in the inheritance graph
|
||||
* @return an iterable
|
||||
*/
|
||||
default Iterable<PermissionHolder> traverse(TraversalAlgorithm algorithm, PermissionHolder startNode) {
|
||||
return GraphTraversers.traverseUsing(algorithm, this, startNode);
|
||||
}
|
||||
|
||||
final class NonContextual implements InheritanceGraph {
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
NonContextual(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends PermissionHolder> successors(PermissionHolder holder) {
|
||||
Set<Group> successors = new TreeSet<>(holder.getInheritanceComparator());
|
||||
List<Node> nodes = holder.getOwnGroupNodes();
|
||||
for (Node n : nodes) {
|
||||
Group g = this.plugin.getGroupManager().getIfLoaded(n.getGroupName());
|
||||
if (g != null) {
|
||||
successors.add(g);
|
||||
}
|
||||
}
|
||||
return successors;
|
||||
}
|
||||
}
|
||||
|
||||
final class Contextual implements InheritanceGraph {
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
/**
|
||||
* The contexts to resolve inheritance in.
|
||||
*/
|
||||
private final Contexts context;
|
||||
|
||||
Contextual(LuckPermsPlugin plugin, Contexts context) {
|
||||
this.plugin = plugin;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends PermissionHolder> successors(PermissionHolder holder) {
|
||||
Set<Group> successors = new TreeSet<>(holder.getInheritanceComparator());
|
||||
List<Node> nodes = holder.getOwnGroupNodes(this.context.getContexts());
|
||||
for (Node n : nodes) {
|
||||
// effectively: if not (we're applying global groups or it's specific anyways)
|
||||
if (!((this.context.isApplyGlobalGroups() || n.isServerSpecific()) && (this.context.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Group g = this.plugin.getGroupManager().getIfLoaded(n.getGroupName());
|
||||
if (g != null) {
|
||||
successors.add(g);
|
||||
}
|
||||
}
|
||||
return successors;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.inheritance;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Provides {@link InheritanceGraph}s.
|
||||
*/
|
||||
public class InheritanceHandler {
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
/**
|
||||
* An inheritance graph which doesn't consider contexts
|
||||
*/
|
||||
private final InheritanceGraph nonContextualGraph;
|
||||
|
||||
/**
|
||||
* Cache of contextual inheritance graph instances
|
||||
*/
|
||||
private final LoadingCache<Contexts, InheritanceGraph> contextualGraphs;
|
||||
|
||||
public InheritanceHandler(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.nonContextualGraph = new InheritanceGraph.NonContextual(plugin);
|
||||
this.contextualGraphs = Caffeine.newBuilder()
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build(key -> new InheritanceGraph.Contextual(this.plugin, key));
|
||||
}
|
||||
|
||||
public InheritanceGraph getGraph() {
|
||||
return this.nonContextualGraph;
|
||||
}
|
||||
|
||||
public InheritanceGraph getGraph(Contexts contexts) {
|
||||
return this.contextualGraphs.get(contexts);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.inheritance.graph;
|
||||
|
||||
/**
|
||||
* A minimal functional interface for graph-structured data.
|
||||
*
|
||||
* @param <N> the node parameter type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Graph<N> {
|
||||
|
||||
/**
|
||||
* Returns all nodes in this graph directly adjacent to {@code node} which
|
||||
* can be reached by traversing {@code node}'s outgoing edges.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code node} is not an element of this graph
|
||||
*/
|
||||
Iterable<? extends N> successors(N node);
|
||||
|
||||
}
|
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 The Guava Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.inheritance.graph;
|
||||
|
||||
import com.google.common.collect.AbstractIterator;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A collection of graph traversal algorithms.
|
||||
*
|
||||
* @author Jens Nyman
|
||||
*/
|
||||
public final class GraphTraversers {
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable {@code Iterable} over the nodes reachable from
|
||||
* {@code startNode}, in the order defined by the {@code algorithm}.
|
||||
*/
|
||||
public static <N> Iterable<N> traverseUsing(TraversalAlgorithm algorithm, Graph<N> graph, N startNode) {
|
||||
Objects.requireNonNull(algorithm, "algorithm");
|
||||
switch (algorithm) {
|
||||
case BREADTH_FIRST:
|
||||
return breadthFirst(graph, startNode);
|
||||
case DEPTH_FIRST_PRE_ORDER:
|
||||
return depthFirstPreOrder(graph, startNode);
|
||||
case DEPTH_FIRST_POST_ORDER:
|
||||
return depthFirstPostOrder(graph, startNode);
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable {@code Iterable} over the nodes reachable from
|
||||
* {@code startNode}, in the order of a breadth-first traversal. That is,
|
||||
* all the nodes of depth 0 are returned, then depth 1, then 2, and so on.
|
||||
*
|
||||
* <p>See <a href="https://en.wikipedia.org/wiki/Breadth-first_search">Wikipedia</a> for more info.</p>
|
||||
*/
|
||||
public static <N> Iterable<N> breadthFirst(Graph<N> graph, N startNode) {
|
||||
Objects.requireNonNull(graph, "graph");
|
||||
Objects.requireNonNull(startNode, "startNode");
|
||||
return () -> new BreadthFirstIterator<>(graph, startNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable {@code Iterable} over the nodes reachable from
|
||||
* {@code startNode}, in the order of a depth-first pre-order traversal.
|
||||
* "Pre-order" implies that nodes appear in the {@code Iterable} in the
|
||||
* order in which they are first visited.
|
||||
*
|
||||
* <p>See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info.</p>
|
||||
*/
|
||||
public static <N> Iterable<N> depthFirstPreOrder(Graph<N> graph, N startNode) {
|
||||
Objects.requireNonNull(graph, "graph");
|
||||
Objects.requireNonNull(startNode, "startNode");
|
||||
return () -> new DepthFirstIterator<>(graph, startNode, Order.PRE_ORDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in
|
||||
* the order of a depth-first post-order traversal. "Post-order" implies that nodes appear in the
|
||||
* {@code Iterable} in the order in which they are visited for the last time.
|
||||
*
|
||||
* <p>See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info.</p>
|
||||
*/
|
||||
public static <N> Iterable<N> depthFirstPostOrder(Graph<N> graph, N startNode) {
|
||||
Objects.requireNonNull(graph, "graph");
|
||||
Objects.requireNonNull(startNode, "startNode");
|
||||
return () -> new DepthFirstIterator<>(graph, startNode, Order.POST_ORDER);
|
||||
}
|
||||
|
||||
private static final class BreadthFirstIterator<N> implements Iterator<N> {
|
||||
private final Graph<N> graph;
|
||||
|
||||
private final Queue<N> queue = new ArrayDeque<>();
|
||||
private final Set<N> visited = new HashSet<>();
|
||||
|
||||
BreadthFirstIterator(Graph<N> graph, N root) {
|
||||
this.graph = graph;
|
||||
this.queue.add(root);
|
||||
this.visited.add(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !this.queue.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public N next() {
|
||||
N current = this.queue.remove();
|
||||
for (N neighbor : this.graph.successors(current)) {
|
||||
if (this.visited.add(neighbor)) {
|
||||
this.queue.add(neighbor);
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DepthFirstIterator<N> extends AbstractIterator<N> {
|
||||
private final Graph<N> graph;
|
||||
|
||||
private final Deque<NodeAndSuccessors> stack = new ArrayDeque<>();
|
||||
private final Set<N> visited = new HashSet<>();
|
||||
private final Order order;
|
||||
|
||||
DepthFirstIterator(Graph<N> graph, N root, Order order) {
|
||||
this.graph = graph;
|
||||
|
||||
// our invariant is that in computeNext we call next on the iterator at the top first, so we
|
||||
// need to start with one additional item on that iterator
|
||||
this.stack.push(withSuccessors(root));
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected N computeNext() {
|
||||
while (true) {
|
||||
if (this.stack.isEmpty()) {
|
||||
return endOfData();
|
||||
}
|
||||
NodeAndSuccessors node = this.stack.getFirst();
|
||||
boolean firstVisit = this.visited.add(node.node);
|
||||
boolean lastVisit = !node.successorIterator.hasNext();
|
||||
boolean produceNode = (firstVisit && this.order == Order.PRE_ORDER) || (lastVisit && this.order == Order.POST_ORDER);
|
||||
if (lastVisit) {
|
||||
this.stack.pop();
|
||||
} else {
|
||||
// we need to push a neighbor, but only if we haven't already seen it
|
||||
N successor = node.successorIterator.next();
|
||||
if (!this.visited.contains(successor)) {
|
||||
this.stack.push(withSuccessors(successor));
|
||||
}
|
||||
}
|
||||
if (produceNode) {
|
||||
return node.node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeAndSuccessors withSuccessors(N node) {
|
||||
return new NodeAndSuccessors(node, this.graph.successors(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple tuple of a node and a partially iterated {@link Iterator} of
|
||||
* its successors
|
||||
*/
|
||||
private final class NodeAndSuccessors {
|
||||
final N node;
|
||||
final Iterator<? extends N> successorIterator;
|
||||
|
||||
NodeAndSuccessors(N node, Iterable<? extends N> successors) {
|
||||
this.node = node;
|
||||
this.successorIterator = successors.iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum Order {
|
||||
PRE_ORDER,
|
||||
POST_ORDER
|
||||
}
|
||||
|
||||
private GraphTraversers() {}
|
||||
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.inheritance.graph;
|
||||
|
||||
public enum TraversalAlgorithm {
|
||||
|
||||
/**
|
||||
* Traverses in breadth-first order.
|
||||
*
|
||||
* <p>That is, all the nodes of depth 0 are returned, then depth 1, then 2, and so on.</p>
|
||||
*
|
||||
* <p>See <a href="https://en.wikipedia.org/wiki/Breadth-first_search">Wikipedia</a> for more info.</p>
|
||||
*/
|
||||
BREADTH_FIRST,
|
||||
|
||||
/**
|
||||
* Traverses in depth-first pre-order.
|
||||
*
|
||||
* <p>"Pre-order" implies that nodes appear in the order in which they are
|
||||
* first visited.</p>
|
||||
*
|
||||
* <p>See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info.</p>
|
||||
*/
|
||||
DEPTH_FIRST_PRE_ORDER,
|
||||
|
||||
/**
|
||||
* Traverses in depth-first post-order.
|
||||
*
|
||||
* <p>"Post-order" implies that nodes appear in the order in which they are
|
||||
* visited for the last time.</p>
|
||||
*
|
||||
* <p>See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info.</p>
|
||||
*/
|
||||
DEPTH_FIRST_POST_ORDER
|
||||
|
||||
}
|
@@ -83,6 +83,15 @@ public final class NodeMap {
|
||||
.treeSetValues(NodeComparator.reverse())
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Copy of {@link #map} which only contains group nodes
|
||||
* @see Node#isGroupNode()
|
||||
*/
|
||||
private final SortedSetMultimap<ImmutableContextSet, Node> inheritanceMap = MultimapBuilder
|
||||
.treeKeys(ContextSetComparator.reverse())
|
||||
.treeSetValues(NodeComparator.reverse())
|
||||
.build();
|
||||
|
||||
/**
|
||||
* The lock which synchronizes the instance
|
||||
*/
|
||||
@@ -143,6 +152,28 @@ public final class NodeMap {
|
||||
}
|
||||
}
|
||||
|
||||
public void copyGroupNodesTo(Collection<? super Node> collection) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
collection.addAll(this.inheritanceMap.values());
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void copyGroupNodesTo(Collection<? super Node> collection, ContextSet filter) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.inheritanceMap.asMap().entrySet()) {
|
||||
if (e.getKey().isSatisfiedBy(filter)) {
|
||||
collection.addAll(e.getValue());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void copyToLocalized(Collection<LocalizedNode> collection) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
@@ -173,17 +204,11 @@ public final class NodeMap {
|
||||
void add(Node node) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.map.put(node.getFullContexts().makeImmutable(), node);
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void replace(Node node, Node previous) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.map.remove(previous.getFullContexts().makeImmutable(), previous);
|
||||
this.map.put(node.getFullContexts().makeImmutable(), node);
|
||||
ImmutableContextSet context = node.getFullContexts().makeImmutable();
|
||||
this.map.put(context, node);
|
||||
if (node.isGroupNode() && node.getValuePrimitive()) {
|
||||
this.inheritanceMap.put(context, node);
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
@@ -192,7 +217,34 @@ public final class NodeMap {
|
||||
void remove(Node node) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.map.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.almostEquals(node));
|
||||
ImmutableContextSet context = node.getFullContexts().makeImmutable();
|
||||
this.map.get(context).removeIf(e -> e.almostEquals(node));
|
||||
if (node.isGroupNode()) {
|
||||
this.inheritanceMap.get(context).removeIf(e -> e.almostEquals(node));
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeExact(Node node) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
ImmutableContextSet context = node.getFullContexts().makeImmutable();
|
||||
this.map.remove(context, node);
|
||||
if (node.isGroupNode() && node.getValuePrimitive()) {
|
||||
this.inheritanceMap.remove(context, node);
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void replace(Node node, Node previous) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
removeExact(previous);
|
||||
add(node);
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
@@ -202,6 +254,7 @@ public final class NodeMap {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.map.clear();
|
||||
this.inheritanceMap.clear();
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
@@ -210,7 +263,9 @@ public final class NodeMap {
|
||||
void clear(ContextSet contextSet) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.map.removeAll(contextSet.makeImmutable());
|
||||
ImmutableContextSet context = contextSet.makeImmutable();
|
||||
this.map.removeAll(context);
|
||||
this.inheritanceMap.removeAll(context);
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
@@ -220,8 +275,9 @@ public final class NodeMap {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.map.clear();
|
||||
this.inheritanceMap.clear();
|
||||
for (Node n : set) {
|
||||
this.map.put(n.getFullContexts().makeImmutable(), n);
|
||||
add(n);
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
@@ -232,7 +288,14 @@ public final class NodeMap {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.map.clear();
|
||||
this.inheritanceMap.clear();
|
||||
|
||||
this.map.putAll(multimap);
|
||||
for (Map.Entry<ImmutableContextSet, Node> entry : this.map.entries()) {
|
||||
if (entry.getValue().isGroupNode() && entry.getValue().getValuePrimitive()) {
|
||||
this.inheritanceMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
@@ -241,7 +304,11 @@ public final class NodeMap {
|
||||
boolean removeIf(Predicate<? super Node> predicate) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
return this.map.values().removeIf(predicate);
|
||||
boolean ret = this.map.values().removeIf(predicate);
|
||||
if (ret) {
|
||||
this.inheritanceMap.values().removeIf(predicate);
|
||||
}
|
||||
return ret;
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
@@ -250,8 +317,13 @@ public final class NodeMap {
|
||||
boolean removeIf(ContextSet contextSet, Predicate<? super Node> predicate) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
SortedSet<Node> nodes = this.map.get(contextSet.makeImmutable());
|
||||
return nodes != null && nodes.removeIf(predicate);
|
||||
ImmutableContextSet context = contextSet.makeImmutable();
|
||||
SortedSet<Node> nodes = this.map.get(context);
|
||||
boolean ret = nodes.removeIf(predicate);
|
||||
if (ret) {
|
||||
this.inheritanceMap.get(context).removeIf(predicate);
|
||||
}
|
||||
return ret;
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
@@ -269,6 +341,9 @@ public final class NodeMap {
|
||||
if (removed != null) {
|
||||
removed.add(entry);
|
||||
}
|
||||
if (entry.isGroupNode() && entry.getValuePrimitive()) {
|
||||
this.inheritanceMap.remove(entry.getFullContexts().makeImmutable(), entry);
|
||||
}
|
||||
work = true;
|
||||
it.remove();
|
||||
}
|
||||
|
@@ -44,6 +44,8 @@ import me.lucko.luckperms.common.caching.HolderCachedData;
|
||||
import me.lucko.luckperms.common.caching.handlers.StateListener;
|
||||
import me.lucko.luckperms.common.caching.type.MetaAccumulator;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceComparator;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceGraph;
|
||||
import me.lucko.luckperms.common.node.ImmutableLocalizedNode;
|
||||
import me.lucko.luckperms.common.node.InheritanceInfo;
|
||||
import me.lucko.luckperms.common.node.MetaType;
|
||||
@@ -51,7 +53,6 @@ import me.lucko.luckperms.common.node.NodeFactory;
|
||||
import me.lucko.luckperms.common.node.NodeTools;
|
||||
import me.lucko.luckperms.common.node.NodeWithContextComparator;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.primarygroup.GroupInheritanceComparator;
|
||||
import me.lucko.luckperms.common.references.GroupReference;
|
||||
import me.lucko.luckperms.common.references.HolderReference;
|
||||
import me.lucko.luckperms.common.references.HolderType;
|
||||
@@ -151,7 +152,7 @@ public abstract class PermissionHolder {
|
||||
/**
|
||||
* Comparator used to ordering groups when calculating inheritance
|
||||
*/
|
||||
private final Comparator<Group> inheritanceComparator = GroupInheritanceComparator.getFor(this);
|
||||
private final Comparator<Group> inheritanceComparator = InheritanceComparator.getFor(this);
|
||||
|
||||
/**
|
||||
* A set of runnables which are called when this objects state changes.
|
||||
@@ -235,6 +236,10 @@ public abstract class PermissionHolder {
|
||||
*/
|
||||
public abstract HolderType getType();
|
||||
|
||||
public Comparator<Group> getInheritanceComparator() {
|
||||
return this.inheritanceComparator;
|
||||
}
|
||||
|
||||
public NodeMap getData(NodeMapType type) {
|
||||
switch (type) {
|
||||
case ENDURING:
|
||||
@@ -296,6 +301,20 @@ public abstract class PermissionHolder {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public List<Node> getOwnGroupNodes() {
|
||||
List<Node> ret = new ArrayList<>();
|
||||
this.transientNodes.copyGroupNodesTo(ret);
|
||||
this.enduringNodes.copyGroupNodesTo(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public List<Node> getOwnGroupNodes(ContextSet filter) {
|
||||
List<Node> ret = new ArrayList<>();
|
||||
this.transientNodes.copyGroupNodesTo(ret, filter);
|
||||
this.enduringNodes.copyGroupNodesTo(ret, filter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public SortedSet<LocalizedNode> getOwnNodesSorted() {
|
||||
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
|
||||
this.transientNodes.copyToLocalized(ret);
|
||||
@@ -323,69 +342,28 @@ public abstract class PermissionHolder {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves inherited nodes and returns them
|
||||
*
|
||||
* @param excludedGroups a list of groups to exclude
|
||||
* @param context context to decide if groups should be applied
|
||||
* @return a set of nodes
|
||||
*/
|
||||
public List<LocalizedNode> resolveInheritances(List<LocalizedNode> accumulator, Set<String> excludedGroups, Contexts context) {
|
||||
if (accumulator == null) {
|
||||
accumulator = new ArrayList<>();
|
||||
}
|
||||
|
||||
if (excludedGroups == null) {
|
||||
excludedGroups = new HashSet<>();
|
||||
}
|
||||
|
||||
if (this.getType().isGroup()) {
|
||||
excludedGroups.add(getObjectName().toLowerCase());
|
||||
}
|
||||
|
||||
// get and add the objects own nodes
|
||||
List<Node> nodes = getOwnNodes(context.getContexts());
|
||||
for (Node node : nodes) {
|
||||
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, getObjectName());
|
||||
accumulator.add(localizedNode);
|
||||
}
|
||||
|
||||
// resolve and process the objects parents
|
||||
List<Group> resolvedGroups = new ArrayList<>();
|
||||
Set<String> processedGroups = new HashSet<>();
|
||||
|
||||
for (Node n : nodes) {
|
||||
if (!n.isGroupNode()) continue;
|
||||
String groupName = n.getGroupName();
|
||||
|
||||
if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue;
|
||||
|
||||
if (!((context.isApplyGlobalGroups() || n.isServerSpecific()) && (context.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Group g = this.plugin.getGroupManager().getIfLoaded(groupName);
|
||||
if (g != null) {
|
||||
resolvedGroups.add(g);
|
||||
private void accumulateInheritancesTo(List<LocalizedNode> accumulator, Contexts context) {
|
||||
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(context);
|
||||
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
|
||||
for (PermissionHolder holder : traversal) {
|
||||
List<Node> nodes = holder.getOwnNodes(context.getContexts());
|
||||
for (Node node : nodes) {
|
||||
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, holder.getObjectName());
|
||||
accumulator.add(localizedNode);
|
||||
}
|
||||
}
|
||||
|
||||
// sort the groups according to weight + other factors.
|
||||
resolvedGroups.sort(this.inheritanceComparator);
|
||||
|
||||
for (Group g : resolvedGroups) {
|
||||
g.resolveInheritances(accumulator, excludedGroups, context);
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
public List<LocalizedNode> resolveInheritances(Contexts context) {
|
||||
return resolveInheritances(null, null, context);
|
||||
List<LocalizedNode> accumulator = new ArrayList<>();
|
||||
accumulateInheritancesTo(accumulator, context);
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
public SortedSet<LocalizedNode> resolveInheritancesAlmostEqual(Contexts contexts) {
|
||||
List<LocalizedNode> nodes = resolveInheritances(new LinkedList<>(), null, contexts);
|
||||
List<LocalizedNode> nodes = new LinkedList<>();
|
||||
accumulateInheritancesTo(nodes, contexts);
|
||||
|
||||
NodeTools.removeAlmostEqual(nodes.iterator());
|
||||
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
|
||||
ret.addAll(nodes);
|
||||
@@ -393,71 +371,37 @@ public abstract class PermissionHolder {
|
||||
}
|
||||
|
||||
public SortedSet<LocalizedNode> resolveInheritancesMergeTemp(Contexts contexts) {
|
||||
List<LocalizedNode> nodes = resolveInheritances(new LinkedList<>(), null, contexts);
|
||||
List<LocalizedNode> nodes = new LinkedList<>();
|
||||
accumulateInheritancesTo(nodes, contexts);
|
||||
|
||||
NodeTools.removeIgnoreValueOrTemp(nodes.iterator());
|
||||
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
|
||||
ret.addAll(nodes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves inherited nodes and returns them
|
||||
*
|
||||
* @param excludedGroups a list of groups to exclude
|
||||
* @return a set of nodes
|
||||
*/
|
||||
public List<LocalizedNode> resolveInheritances(List<LocalizedNode> accumulator, Set<String> excludedGroups) {
|
||||
if (accumulator == null) {
|
||||
accumulator = new ArrayList<>();
|
||||
}
|
||||
|
||||
if (excludedGroups == null) {
|
||||
excludedGroups = new HashSet<>();
|
||||
}
|
||||
|
||||
if (this.getType().isGroup()) {
|
||||
excludedGroups.add(getObjectName().toLowerCase());
|
||||
}
|
||||
|
||||
// get and add the objects own nodes
|
||||
List<Node> nodes = getOwnNodes();
|
||||
for (Node node : nodes) {
|
||||
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, getObjectName());
|
||||
accumulator.add(localizedNode);
|
||||
}
|
||||
|
||||
// resolve and process the objects parents
|
||||
List<Group> resolvedGroups = new ArrayList<>();
|
||||
Set<String> processedGroups = new HashSet<>();
|
||||
|
||||
for (Node n : nodes) {
|
||||
if (!n.isGroupNode()) continue;
|
||||
String groupName = n.getGroupName();
|
||||
|
||||
if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue;
|
||||
|
||||
Group g = this.plugin.getGroupManager().getIfLoaded(groupName);
|
||||
if (g != null) {
|
||||
resolvedGroups.add(g);
|
||||
private void accumulateInheritancesTo(List<LocalizedNode> accumulator) {
|
||||
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph();
|
||||
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
|
||||
for (PermissionHolder holder : traversal) {
|
||||
List<Node> nodes = holder.getOwnNodes();
|
||||
for (Node node : nodes) {
|
||||
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, holder.getObjectName());
|
||||
accumulator.add(localizedNode);
|
||||
}
|
||||
}
|
||||
|
||||
// sort the groups according to weight + other factors.
|
||||
resolvedGroups.sort(this.inheritanceComparator);
|
||||
|
||||
for (Group g : resolvedGroups) {
|
||||
g.resolveInheritances(accumulator, excludedGroups);
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
public List<LocalizedNode> resolveInheritances() {
|
||||
return resolveInheritances(null, null);
|
||||
List<LocalizedNode> accumulator = new ArrayList<>();
|
||||
accumulateInheritancesTo(accumulator);
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
public SortedSet<LocalizedNode> resolveInheritancesAlmostEqual() {
|
||||
List<LocalizedNode> nodes = resolveInheritances(new LinkedList<>(), null);
|
||||
List<LocalizedNode> nodes = new LinkedList<>();
|
||||
accumulateInheritancesTo(nodes);
|
||||
|
||||
NodeTools.removeAlmostEqual(nodes.iterator());
|
||||
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
|
||||
ret.addAll(nodes);
|
||||
@@ -465,19 +409,20 @@ public abstract class PermissionHolder {
|
||||
}
|
||||
|
||||
public SortedSet<LocalizedNode> resolveInheritancesMergeTemp() {
|
||||
List<LocalizedNode> nodes = resolveInheritances(new LinkedList<>(), null);
|
||||
List<LocalizedNode> nodes = new LinkedList<>();
|
||||
accumulateInheritancesTo(nodes);
|
||||
|
||||
NodeTools.removeIgnoreValueOrTemp(nodes.iterator());
|
||||
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
|
||||
ret.addAll(nodes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public SortedSet<LocalizedNode> getAllNodes(Contexts context) {
|
||||
List<LocalizedNode> entries;
|
||||
private List<LocalizedNode> getAllEntries(Contexts context) {
|
||||
List<LocalizedNode> entries = new LinkedList<>();
|
||||
if (context.isApplyGroups()) {
|
||||
entries = resolveInheritances(new LinkedList<>(), null, context);
|
||||
accumulateInheritancesTo(entries, context);
|
||||
} else {
|
||||
entries = new LinkedList<>();
|
||||
for (Node n : getOwnNodes(context.getContexts())) {
|
||||
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName());
|
||||
entries.add(localizedNode);
|
||||
@@ -491,6 +436,12 @@ public abstract class PermissionHolder {
|
||||
entries.removeIf(n -> !n.isGroupNode() && !n.isWorldSpecific());
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
public SortedSet<LocalizedNode> getAllNodes(Contexts context) {
|
||||
List<LocalizedNode> entries = getAllEntries(context);
|
||||
|
||||
NodeTools.removeSamePermission(entries.iterator());
|
||||
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
|
||||
ret.addAll(entries);
|
||||
@@ -498,23 +449,7 @@ public abstract class PermissionHolder {
|
||||
}
|
||||
|
||||
public Map<String, Boolean> exportNodesAndShorthand(Contexts context, boolean lowerCase) {
|
||||
List<LocalizedNode> entries;
|
||||
if (context.isApplyGroups()) {
|
||||
entries = resolveInheritances(new LinkedList<>(), null, context);
|
||||
} else {
|
||||
entries = new LinkedList<>();
|
||||
for (Node n : getOwnNodes(context.getContexts())) {
|
||||
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName());
|
||||
entries.add(localizedNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (!context.isIncludeGlobal()) {
|
||||
entries.removeIf(n -> !n.isGroupNode() && !n.isServerSpecific());
|
||||
}
|
||||
if (!context.isApplyGlobalWorldGroups()) {
|
||||
entries.removeIf(n -> !n.isGroupNode() && !n.isWorldSpecific());
|
||||
}
|
||||
List<LocalizedNode> entries = getAllEntries(context);
|
||||
|
||||
Map<String, Boolean> perms = new HashMap<>();
|
||||
boolean applyShorthand = this.plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND);
|
||||
@@ -557,117 +492,55 @@ public abstract class PermissionHolder {
|
||||
return ImmutableMap.copyOf(perms);
|
||||
}
|
||||
|
||||
public MetaAccumulator accumulateMeta(MetaAccumulator accumulator, Set<String> excludedGroups, Contexts context) {
|
||||
public MetaAccumulator accumulateMeta(MetaAccumulator accumulator, Contexts context) {
|
||||
if (accumulator == null) {
|
||||
accumulator = MetaAccumulator.makeFromConfig(this.plugin);
|
||||
}
|
||||
|
||||
if (excludedGroups == null) {
|
||||
excludedGroups = new HashSet<>();
|
||||
}
|
||||
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(context);
|
||||
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
|
||||
for (PermissionHolder holder : traversal) {
|
||||
List<Node> nodes = holder.getOwnNodes(context.getContexts());
|
||||
for (Node node : nodes) {
|
||||
if (!node.getValuePrimitive()) continue;
|
||||
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
|
||||
|
||||
if (this.getType().isGroup()) {
|
||||
excludedGroups.add(getObjectName().toLowerCase());
|
||||
}
|
||||
if (!((context.isIncludeGlobal() || node.isServerSpecific()) && (context.isIncludeGlobalWorld() || node.isWorldSpecific()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get and add the objects own nodes
|
||||
List<Node> nodes = getOwnNodes(context.getContexts());
|
||||
|
||||
for (Node node : nodes) {
|
||||
if (!node.getValuePrimitive()) continue;
|
||||
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
|
||||
|
||||
if (!((context.isIncludeGlobal() || node.isServerSpecific()) && (context.isIncludeGlobalWorld() || node.isWorldSpecific()))) {
|
||||
continue;
|
||||
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, holder.getObjectName()));
|
||||
}
|
||||
|
||||
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, getObjectName()));
|
||||
}
|
||||
|
||||
OptionalInt w = getWeight();
|
||||
if (w.isPresent()) {
|
||||
accumulator.accumulateWeight(w.getAsInt());
|
||||
}
|
||||
|
||||
// resolve and process the objects parents
|
||||
List<Group> resolvedGroups = new ArrayList<>();
|
||||
Set<String> processedGroups = new HashSet<>();
|
||||
|
||||
for (Node n : nodes) {
|
||||
if (!n.isGroupNode()) continue;
|
||||
String groupName = n.getGroupName();
|
||||
|
||||
if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue;
|
||||
|
||||
if (!((context.isApplyGlobalGroups() || n.isServerSpecific()) && (context.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) {
|
||||
continue;
|
||||
OptionalInt w = holder.getWeight();
|
||||
if (w.isPresent()) {
|
||||
accumulator.accumulateWeight(w.getAsInt());
|
||||
}
|
||||
|
||||
Group g = this.plugin.getGroupManager().getIfLoaded(groupName);
|
||||
if (g != null) {
|
||||
resolvedGroups.add(g);
|
||||
}
|
||||
}
|
||||
|
||||
// sort the groups according to weight + other factors.
|
||||
resolvedGroups.sort(this.inheritanceComparator);
|
||||
|
||||
for (Group g : resolvedGroups) {
|
||||
g.accumulateMeta(accumulator, excludedGroups, context);
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
public MetaAccumulator accumulateMeta(MetaAccumulator accumulator, Set<String> excludedGroups) {
|
||||
public MetaAccumulator accumulateMeta(MetaAccumulator accumulator) {
|
||||
if (accumulator == null) {
|
||||
accumulator = MetaAccumulator.makeFromConfig(this.plugin);
|
||||
}
|
||||
|
||||
if (excludedGroups == null) {
|
||||
excludedGroups = new HashSet<>();
|
||||
}
|
||||
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph();
|
||||
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
|
||||
for (PermissionHolder holder : traversal) {
|
||||
List<Node> nodes = holder.getOwnNodes();
|
||||
for (Node node : nodes) {
|
||||
if (!node.getValuePrimitive()) continue;
|
||||
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
|
||||
|
||||
if (this.getType().isGroup()) {
|
||||
excludedGroups.add(getObjectName().toLowerCase());
|
||||
}
|
||||
|
||||
// get and add the objects own nodes
|
||||
List<Node> nodes = getOwnNodes();
|
||||
|
||||
for (Node node : nodes) {
|
||||
if (!node.getValuePrimitive()) continue;
|
||||
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
|
||||
|
||||
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, getObjectName()));
|
||||
}
|
||||
|
||||
OptionalInt w = getWeight();
|
||||
if (w.isPresent()) {
|
||||
accumulator.accumulateWeight(w.getAsInt());
|
||||
}
|
||||
|
||||
// resolve and process the objects parents
|
||||
List<Group> resolvedGroups = new ArrayList<>();
|
||||
Set<String> processedGroups = new HashSet<>();
|
||||
|
||||
for (Node n : nodes) {
|
||||
if (!n.isGroupNode()) continue;
|
||||
String groupName = n.getGroupName();
|
||||
|
||||
if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue;
|
||||
|
||||
Group g = this.plugin.getGroupManager().getIfLoaded(groupName);
|
||||
if (g != null) {
|
||||
resolvedGroups.add(g);
|
||||
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, holder.getObjectName()));
|
||||
}
|
||||
}
|
||||
|
||||
// sort the groups according to weight + other factors.
|
||||
resolvedGroups.sort(this.inheritanceComparator);
|
||||
|
||||
for (Group g : resolvedGroups) {
|
||||
g.accumulateMeta(accumulator, excludedGroups);
|
||||
OptionalInt w = getWeight();
|
||||
if (w.isPresent()) {
|
||||
accumulator.accumulateWeight(w.getAsInt());
|
||||
}
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
@@ -1019,8 +892,7 @@ public abstract class PermissionHolder {
|
||||
}
|
||||
|
||||
public Set<HolderReference> getGroupReferences() {
|
||||
return getOwnNodes().stream()
|
||||
.filter(Node::isGroupNode)
|
||||
return getOwnGroupNodes().stream()
|
||||
.map(Node::getGroupName)
|
||||
.map(String::toLowerCase)
|
||||
.map(GroupReference::of)
|
||||
|
@@ -41,6 +41,7 @@ import me.lucko.luckperms.common.contexts.ContextManager;
|
||||
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
|
||||
import me.lucko.luckperms.common.event.EventFactory;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceHandler;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.logging.Logger;
|
||||
import me.lucko.luckperms.common.managers.group.GroupManager;
|
||||
@@ -178,6 +179,13 @@ public interface LuckPermsPlugin {
|
||||
*/
|
||||
ContextManager<?> getContextManager();
|
||||
|
||||
/**
|
||||
* Gets the inheritance handler
|
||||
*
|
||||
* @return the inheritance handler
|
||||
*/
|
||||
InheritanceHandler getInheritanceHandler();
|
||||
|
||||
/**
|
||||
* Gets the cached state manager for the platform.
|
||||
*
|
||||
|
@@ -25,15 +25,16 @@
|
||||
|
||||
package me.lucko.luckperms.common.primarygroup;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceGraph;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class AllParentsByWeightHolder extends CachedPrimaryGroupHolder {
|
||||
public AllParentsByWeightHolder(User user) {
|
||||
@@ -47,23 +48,21 @@ public class AllParentsByWeightHolder extends CachedPrimaryGroupHolder {
|
||||
contexts = this.user.getPlugin().getContextManager().getStaticContexts();
|
||||
}
|
||||
|
||||
// hack to get a list of groups the holder is inheriting from
|
||||
Set<String> groupNames = new LinkedHashSet<>();
|
||||
this.user.resolveInheritances(new NoopList<>(), groupNames, contexts);
|
||||
InheritanceGraph graph = this.user.getPlugin().getInheritanceHandler().getGraph(contexts);
|
||||
|
||||
List<Group> groups = new ArrayList<>();
|
||||
for (String groupName : groupNames) {
|
||||
Group group = this.user.getPlugin().getGroupManager().getIfLoaded(groupName);
|
||||
if (group != null) {
|
||||
groups.add(group);
|
||||
}
|
||||
}
|
||||
// fully traverse the graph, obtain a list of permission holders the user inherits from
|
||||
List<PermissionHolder> traversal = ImmutableList.copyOf(graph.traverse(this.user.getPlugin().getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this.user));
|
||||
|
||||
Group bestGroup = null;
|
||||
|
||||
if (!groups.isEmpty()) {
|
||||
if (!traversal.isEmpty()) {
|
||||
int best = 0;
|
||||
for (Group g : groups) {
|
||||
for (PermissionHolder holder : traversal) {
|
||||
if (!(holder instanceof Group)) {
|
||||
continue;
|
||||
}
|
||||
Group g = ((Group) holder);
|
||||
|
||||
int weight = g.getWeight().orElse(0);
|
||||
if (bestGroup == null || g.getWeight().orElse(0) > best) {
|
||||
bestGroup = g;
|
||||
@@ -74,22 +73,4 @@ public class AllParentsByWeightHolder extends CachedPrimaryGroupHolder {
|
||||
|
||||
return bestGroup == null ? null : bestGroup.getName();
|
||||
}
|
||||
|
||||
private static final class NoopList<E> extends AbstractList<E> implements List<E> {
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -51,6 +51,7 @@ import me.lucko.luckperms.common.dependencies.DependencyRegistry;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.ReflectionClassLoader;
|
||||
import me.lucko.luckperms.common.event.EventFactory;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceHandler;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.NoopLocaleManager;
|
||||
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
|
||||
@@ -179,6 +180,7 @@ public class LPSpongePlugin implements LuckPermsSpongePlugin {
|
||||
private LocaleManager localeManager;
|
||||
private PluginClassLoader pluginClassLoader;
|
||||
private DependencyManager dependencyManager;
|
||||
private InheritanceHandler inheritanceHandler;
|
||||
private CachedStateManager cachedStateManager;
|
||||
private ContextManager<Subject> contextManager;
|
||||
private CalculatorFactory calculatorFactory;
|
||||
@@ -242,6 +244,7 @@ public class LPSpongePlugin implements LuckPermsSpongePlugin {
|
||||
|
||||
// load internal managers
|
||||
getLog().info("Loading internal permission managers...");
|
||||
this.inheritanceHandler = new InheritanceHandler(this);
|
||||
this.userManager = new SpongeUserManager(this);
|
||||
this.groupManager = new SpongeGroupManager(this);
|
||||
this.trackManager = new StandardTrackManager(this);
|
||||
@@ -609,6 +612,11 @@ public class LPSpongePlugin implements LuckPermsSpongePlugin {
|
||||
return this.contextManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InheritanceHandler getInheritanceHandler() {
|
||||
return this.inheritanceHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatorFactory getCalculatorFactory() {
|
||||
return this.calculatorFactory;
|
||||
|
@@ -376,7 +376,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
|
||||
|
||||
toRemove.forEach(makeUnsetConsumer(this.enduring));
|
||||
|
||||
MetaAccumulator metaAccumulator = this.holder.accumulateMeta(null, null, this.service.getPlugin().getContextManager().formContexts(contexts));
|
||||
MetaAccumulator metaAccumulator = this.holder.accumulateMeta(null, this.service.getPlugin().getContextManager().formContexts(contexts));
|
||||
int priority = metaAccumulator.getChatMeta(type).keySet().stream().mapToInt(e -> e).max().orElse(0);
|
||||
priority += 10;
|
||||
|
||||
|
@@ -128,6 +128,18 @@ apply-sponge-implicit-wildcards=true
|
||||
# false, LuckPerms will ignore this data when considering if a player has a permission.
|
||||
apply-sponge-default-subjects=true
|
||||
|
||||
# The algorithm LuckPerms should use when traversing the "inheritance tree".
|
||||
#
|
||||
# The valid options are:
|
||||
# - breadth-first
|
||||
# - depth-first-pre-order
|
||||
# - depth-first-post-order
|
||||
#
|
||||
# See here for information about the differences between each algorithm.
|
||||
# - https://en.wikipedia.org/wiki/Breadth-first_search
|
||||
# - https://en.wikipedia.org/wiki/Depth-first_search
|
||||
inheritance-traversal-algorithm="depth-first-pre-order"
|
||||
|
||||
# Define special group weights for this server.
|
||||
# Default is just 0.
|
||||
group-weight {
|
||||
|
Reference in New Issue
Block a user