LuckPerms

LuckPerms

41.4k Downloads

Config option to choose between depth-first and breadth-first inheritance resolution

mibby opened this issue · 16 comments

commented

LuckPerms-Bukkit-4.0.117 (dev 516)
Paper dev 1317 (Spigot 1.12.2)

@lucko Global permissions seem to have a higher priority than individual world ones. I have a group that is given worldedit permissions. These permissions are given globally so they can use worldedit in any world. I also negate the worldedit permissions in a specific world with a wildcard because I do not want those players to be able to use worldedit in that world. However, they are still able to make a selection with the worldedit wand, as well as use worldedit commands (i.e. /cut) in that world.

LP verbose reports they still have permission to use that worldedit command / utility. Thus this leads me to believe that the global inherited permissions are receiving a higher priority over individual world defined ones.

Group permissions.
https://paste.ubuntu.com/26468886/

commented

Specific permissions override wildcard permissions. See this page for all details: https://github.com/lucko/LuckPerms/wiki/Advanced-Setup#permission-calculation

commented

So world permissions will override generic permissions, but not in the case of if the world permission is a wildcard and the generic one isn't? Hmm.

commented

That is correct. As generic permissions always take priority.
The page I liked explains it in detail and what other factors play in

commented

@BrainStone I see, thanks for explaining. I guess I'll have to not use a wildcard to bulk negate in individual worlds then.

However I seem to be running into another issue. Are permissions given higher up in the inheritance tree not taking priority / overriding lower rank ones?

Member rank has the following permission.

name: member
permissions:
- essentials.build:
    value: false
    world: experimental
- mcmmo.ability.acrobatics.gracefulroll:
    value: false
- mcmmo.ability.axes.skullsplitter:
    value: false
- mcmmo.ability.blastmining.all:
    value: false
- mcmmo.ability.excavation.gigadrillbreaker:
    value: false
- mcmmo.ability.herbalism.greenterra:
    value: false
- mcmmo.ability.mining.blastmining.all:
    value: false
- mcmmo.ability.mining.superbreaker:
    value: false
- mcmmo.ability.swords.serratedstrikes:
    value: false
- mcmmo.ability.unarmed.berserk:
    value: false
- mcmmo.ability.unarmed.blockcracker:
    value: false
- mcmmo.ability.woodcutting.treefeller:
    value: false
- meta.weight.15
- weight.5

Donor rank has the following permission. Inheriting member rank for 'default' permissions.

name: donor
permissions:
- essentials.build:
    value: true
    world: experimental
- mcmmo.ability.acrobatics.gracefulroll:
    value: true
    world: experimental
- mcmmo.ability.axes.skullsplitter:
    value: true
    world: experimental
- mcmmo.ability.blastmining.all:
    value: true
    world: experimental
- mcmmo.ability.excavation.gigadrillbreaker:
    value: true
    world: experimental
- mcmmo.ability.herbalism.greenterra:
    value: true
    world: experimental
- mcmmo.ability.mining.blastmining.all:
    value: true
    world: experimental
- mcmmo.ability.mining.superbreaker:
    value: true
    world: experimental
- mcmmo.ability.swords.serratedstrikes:
    value: true
    world: experimental
- mcmmo.ability.unarmed.berserk:
    value: true
    world: experimental
- mcmmo.ability.unarmed.blockcracker:
    value: true
    world: experimental
- mcmmo.ability.woodcutting.treefeller:
    value: true
    world: experimental
- group.member
- weight.5
- meta.weight.15
parents:
- member

Builder rank has the following permissions. Inheriting member rank for 'default' permissions.

name: builder
permissions:
- meta.weight.15
- weight.5
parents:
- member
  • If a person is only ranked member, they can not use mcmmo abilities or build in the world experimental.
  • If a person is only ranked donor, they can build in the experimental world and use mcmmo abilities there.
  • If a person is ranked donor and builder and has donor set as their primary group, they can build and use abilities.
  • If a person is ranked donor and builder and has builder set as their primary group, they cannot build or use abilities. Despite the donor rank giving permission.

My rank tree looks like this.
Member
Member -> Donor1 -> Donor2 -> Donor3
Member -> Builder1 -> Builder2 -> Builder3

Players can have either one or both a donor and builder rank. Those rank trees inherit the member rank as a fallback to ensure they have default permissions in case they get set to the rank and no longer have member as part of their grouping. Players are allowed to switchprimarygroup their groups to have the primary group of their choice set for color and prefix. (i.e. displaying builder prefix instead of donor) However if they have a builder rank set as their primary group, the permissions granted to donor (to override negation in member) no longer take effect.

I match the weight values so one rank prefix doesn't take priority over another and is instead displayed based on what the person's primary group is set to. As well as to not sort players in my tab list based on weight so everyone is in alphabetical order, while only using weight to have staff ranks shown at the top.

Edit: To clarify, I believe this is caused by if the primary group set inherits member again, it does not take into account other ranks you may have that are higher up in their inheritance tree that should override member permissions.

commented

You should set the weights correctly
The higher the rank, the higher the weight should be.

commented

Except I can't set the weight without affecting other things. If one rank has a higher weight value over another, that group's prefix is forced over the former. This means players can't choose their group prefix/color they want shown in chat by switching their primary group. It also would sort players in my tab list by weight instead of alphabetical order, as I do use weight to have staff sorted at the top, then everyone else being equal.

@lucko Perhaps you know of a solution to the problem with inherited groups not overriding lesser rank perms when not set as the primary group when the existing primary group re-inherits the lesser rank? :(

commented

I'm too tired to read this properly right now, so if it's ok I'll respond tomorrow when I'm more awake :)

commented

Weights have nothing to do with prefixes. They only control the priority of inheritance

commented

I'm too tired to read this properly right now, so if it's ok I'll respond tomorrow :)

Sure thing. :)

Weights have nothing to do with prefixes. They only control the priority of inheritance

Except it does and is how I force staff to have their staff prefix / color?

Weight - this is just a number which determines the priority of the prefix/suffix. A higher number = a higher weight and a higher priority.

https://github.com/lucko/LuckPerms/wiki/Prefixes,-Suffixes-&-Meta


Edit: I seem to be confusing myself a bit since there are 3 separate weight values used for different things. Meta, main, and prefix. Meta for other plugin priority (i.e. tab priority in BungeeTabListPlus), prefix for prefix priority / overriding (unless setting primary group with a group that has equal priority to specify one over another), and main for permission priority.

However, lowering the member rank "main" weight, the issue still persists. Member has a main weight of 2, donor and builder both have a main weight of 5. When builder is the primary group, permissions granted from donor aren't overriding the member negation. Even with donor weight set higher than builder but builder being the primary group, it doesn't allow it.

My guess is because the builder rank is set as the primary group and inherits member as a fallback in case users have no other groups, inheritance from member again (from the primary group) is taking priority?


Edit2: The reason why I have all donor and builder groups with the same weight value is because it forces the primary group to the group with the highest weight. I do not want to force a donor rank over someone who wants to have a builder rank displayed as their primary instead. But I do want to force primary group if they have a staff rank, hence why I have staff ranks set to a higher weight values. When someone's primary group is changed, their chat tag is changed with it since my chat plugin placeholder reads {vault_rank} from their primary group.

# Available Options:
# -> stored                 use the value stored against the users record in the file/database
#                           directly and indirectly
# -> parents-by-weight      just use the users most highly weighted parent
# -> all-parents-by-weight  same as above, but calculates based upon all parents inherited from both
primary-group-calculation: all-parents-by-weight

all-parents-by-weight is used so staff ranks can be forced primary, with other ranks being equal in weight so primary is determined based on what was last set with setprimarygroup - so players can manually swap prefix & chat tag based on what groups they currently have.

So I guess the question is, shouldn't groups you have that inherit other groups take a higher priority up the inheritance chain for permission overriding?

Inheritance tree example. Member rank negates permissions in the specified world, Donor gives those permissions back in the specified world.
( ) = Groups player has| [#] = group's set weight

Member [2] > Donor [5] > (Donor2 [5]) > Donor3 [5]
Member [2] > (Builder [5]) > Builder2 [5]

Player can't use abilities / build in world.

User Info: Name
- Primary Group: Builder
- Parent Groups:
> Donor2
> Builder

Player can use abilities / build in world.

User Info: Name
- Primary Group: Donor2
- Parent Groups:
> Donor2
> Builder

For reference, PEX doesn't have this issue since permissions were handled more on a ladder-type overridance for inheritance.

commented

I was talking about group weights. Not prefix/suffix weights. M

commented

@BrainStone

I was talking about group weights. Not prefix/suffix weights. M

Well even lowering member group weight down lower than every other group, it doesn't override the permission inherited from a higher group when that group is not primary group. i.e. Builder set as primary (which inherits from member) with donor set as just another parent group (which also inherits from member but should override perms).

Also from above;

When someone's primary group is changed, their chat tag is changed with it since my chat plugin placeholder reads {vault_rank} from their primary group.

So the group weight does play into tag prefix in my environment since my chat plugin is grabbing the player's primary group to display current tag. I use the normal prefix/suffix weight for the color code in tab and displayname -- also equal weight so it is determined based on primary group of player's choosing.

@lucko If it isn't done so already, assuming this isn't just a logical error when lesser groups are re-inherited again under the primary group but other parent groups should override, I propose possibly having an arbitrary inheritance order when inheriting groups via group.<name>. So all permissions inherited from the group.name have a lower priority over permissions inherited upstream by the parent group actually set to.

Rough example.

  • Member [priority 1]
    Absolute priority of permissions (or ignored priority) since it is the only group.

  • Member [priority 1] > Builder [priority 2]
    Builder inherits a higher priority than member because it is higher up the permission inheritance. Inherits member from group.member permission.

  • Member [priority 1] > Donor [priority 2] > Donor2 [priority 3]
    Similarly to above, the higher up the donor rank you are, the higher priority your current group permissions should be over the inherited lower ranks from group.name permission.

That way if you have the parent groups Donor2 and Builder, but Builder is set as the primary group for prefix, Donor2 permissions would override the inherited member permissions since it has a higher priority? An actual order / ladder to group inheritance.

Again just to clarify the problem I'm experiencing in full, I currently have the member group weight set to 2 with all other groups higher than it set to 5 so chat tag can be determined based on what the person wants to display as, based on their primary group of choosing. With member weight set lower than the rest, parent groups granted on users that should override member permissions, are not overriding the member permissions when a group other than the one that overrides permissions is set as primary.

commented

The solution to your problem is to set different weight values, letting you determine the inheritance order.

I really appreciate the detail, but it's kinda hard to follow your examples. :p

Regarding world specific permissions vs inherited permissions, take the following example.

User luck inherits from group admin, which inherits from group mod. Permissions are ordered like:

  1. luck's world specific permissions
  2. luck's global permissions
  3. admin's world specific permissions
  4. admin's global permissions
  5. mod's world specific permissions
  6. mod's global permissions

(obviously more categories of permissions are slotted in - i've simplified it for the sake of explaining)

Within the list formed above, there might be duplicate copies of the same "permission" (like the actual permission string)

This list is then flattened, by taking each element in the original list, and adding it to a new list only if the new list doesn't contain the same "actual" permission already.

This is why, even if you negate something in a specific world at a group level, it can be overridden by a global setting lower down the inheritance tree.

(worth noting that the way users inherit stuff isn't special - the process happens recursively and in the same way for both users & groups)

Naturally, users are just always at the bottom of the inheritance tree, because LuckPerms doesn't let groups inherit from users.

commented

The solution to your problem is to set different weight values, letting you determine the inheritance order.

But with the member group set to a lower weight value than other ranks, shouldn't other parent groups that grant that same permission in the world take priority? This is not the case when a group that re-inherits member is set as the primary group.

I keep all ranks that aren't member and staff at the same weight so player's prefix and tag (which is handled by {vault_rank}) is defined based on what their apiary group of choosing is set to.

commented

This is not the case when a group that re-inherits member is set as the primary group.

What do you mean by this?

I keep all ranks that aren't member and staff at the same weight so player's prefix and tag (which is handled by {vault_rank}) is defined based on what their apiary group of choosing is set to.

If the weights are the same, the order isn't specified, which is most likely why you're experiencing issues.

commented

What do you mean by this?

Inheritance tree example. Member rank negates permissions in the specified world, Donor gives those permissions back in the specified world.
( ) = Groups player has | [#] = group's set weight

Member [2] > (Donor [5]) > Donor2 [5]
Member [2] > (Builder [5]) > Builder2 [5]

The player's primary group is Builder. But they also have a parent group of Donor. When Builder is the primary group, permissions granted from Donor don't override member negation. Builder re-inherits member permissions via group.member. When Donor is the primary group, there is no issue.

commented

Ah okay, I see the issue then.

LuckPerms currently uses depth-first search to resolve inheritance rules.

https://en.wikipedia.org/wiki/Depth-first_search

This means that when the primary group is builder, the inheritance is resolved in this order:

Player's own permissions, then builder, then member, then donor.

This is because the deeper inheritance tree behind the builder group (in this case member) is resolved, before the algorithm backtracks and checks on donor.

What you're effectively requesting is an option to use a breadth-first search.

https://en.wikipedia.org/wiki/Breadth-first_search

I think that's a valid request - but it will require quite a bit of work to support it.