diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index 10c40146..b791931d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -60,11 +60,13 @@ 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 java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -205,6 +207,11 @@ public abstract class PermissionHolder { @Getter private final Lock ioLock = new ReentrantLock(); + /** + * Comparator used to ordering groups when calculating inheritance + */ + private final Comparator inheritanceComparator = GroupInheritanceComparator.getFor(this); + /** * A set of runnables which are called when this objects state changes. */ @@ -559,10 +566,8 @@ public abstract class PermissionHolder { } } - resolvedGroups.sort((o1, o2) -> { - int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); - return result == 1 ? -1 : 1; - }); + // sort the groups according to weight + other factors. + resolvedGroups.sort(inheritanceComparator); for (Group g : resolvedGroups) { g.resolveInheritances(accumulator, excludedGroups, context); @@ -633,10 +638,8 @@ public abstract class PermissionHolder { } } - resolvedGroups.sort((o1, o2) -> { - int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); - return result == 1 ? -1 : 1; - }); + // sort the groups according to weight + other factors. + resolvedGroups.sort(inheritanceComparator); for (Group g : resolvedGroups) { g.resolveInheritances(accumulator, excludedGroups); @@ -806,10 +809,8 @@ public abstract class PermissionHolder { } } - resolvedGroups.sort((o1, o2) -> { - int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); - return result == 1 ? -1 : 1; - }); + // sort the groups according to weight + other factors. + resolvedGroups.sort(inheritanceComparator); for (Group g : resolvedGroups) { g.accumulateMeta(accumulator, excludedGroups, context); @@ -862,10 +863,8 @@ public abstract class PermissionHolder { } } - resolvedGroups.sort((o1, o2) -> { - int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); - return result == 1 ? -1 : 1; - }); + // sort the groups according to weight + other factors. + resolvedGroups.sort(inheritanceComparator); for (Group g : resolvedGroups) { g.accumulateMeta(accumulator, excludedGroups); diff --git a/common/src/main/java/me/lucko/luckperms/common/primarygroup/GroupInheritanceComparator.java b/common/src/main/java/me/lucko/luckperms/common/primarygroup/GroupInheritanceComparator.java new file mode 100644 index 00000000..461b229c --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/primarygroup/GroupInheritanceComparator.java @@ -0,0 +1,75 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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.primarygroup; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; + +import me.lucko.luckperms.common.model.Group; +import me.lucko.luckperms.common.model.PermissionHolder; +import me.lucko.luckperms.common.model.User; + +import java.util.Comparator; + +/** + * Determines the order of group inheritance in {@link PermissionHolder}. + */ +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class GroupInheritanceComparator implements Comparator { + private static final Comparator NULL_ORIGIN = new GroupInheritanceComparator(null); + + public static Comparator getFor(PermissionHolder origin) { + if (origin instanceof User) { + return new GroupInheritanceComparator(((User) origin)); + } + return NULL_ORIGIN; + } + + private final User origin; + + @Override + public int compare(Group o1, Group o2) { + int ret = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); + if (ret != 0) { + // note negated value - we want higher weights first! + return -ret; + } + + // failing differing group weights, check if one of the groups is a primary group + if (origin != null) { + boolean o1Primary = o1.getName().equalsIgnoreCase(origin.getPrimaryGroup().getStoredValue()); + boolean o2Primary = o2.getName().equalsIgnoreCase(origin.getPrimaryGroup().getStoredValue()); + + // one of them is a primary group, and therefore has priority + if (o1Primary != o2Primary) { + return o1Primary ? 1 : -1; + } + } + + // fallback to string based comparison + return o1.getName().compareTo(o2.getName()); + } +}