diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java index 8c37986b..7c9639de 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java @@ -330,7 +330,7 @@ public class VaultChatHook extends Chat { world = perms.correctWorld(world); perms.log("Getting meta: '" + node + "' for group " + group.getName() + " on world " + world + ", server " + perms.getServer()); - for (Node n : group.mergePermissionsToList()) { + for (Node n : group.getOwnNodes()) { if (!n.getValue()) continue; if (!n.isMeta()) continue; if (!n.shouldApplyWithContext(perms.createContextForWorldLookup(world).getContexts())) continue; diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java index 7dee2417..0a5d3581 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java @@ -225,7 +225,7 @@ public class VaultPermissionHook extends Permission { if (group == null) return false; // This is a nasty call. Groups aren't cached. :( - Map permissions = group.exportNodes(ExtractedContexts.generate(createContextForWorldLookup(world)), true); + Map permissions = group.exportNodesAndShorthand(ExtractedContexts.generate(createContextForWorldLookup(world)), true); return permissions.containsKey(permission.toLowerCase()) && permissions.get(permission.toLowerCase()); } @@ -350,7 +350,7 @@ public class VaultPermissionHook extends Permission { } String w = world; // screw effectively final - return user.getNodes().values().stream() + return user.getEnduringNodes().values().stream() .filter(Node::isGroupNode) .filter(n -> n.shouldApplyWithContext(createContextForWorldLookup(player, w).getContexts())) .map(Node::getGroupName) @@ -418,7 +418,7 @@ public class VaultPermissionHook extends Permission { } } else { // we need to check the users permissions only - for (Node node : user.mergePermissionsToList()) { + for (Node node : user.getOwnNodes()) { if (!node.getValue()) continue; if (!node.getPermission().toLowerCase().startsWith("vault.primarygroup.")) continue; if (!node.shouldApplyOnServer(getServer(), isIncludeGlobal(), false)) continue; @@ -433,7 +433,7 @@ public class VaultPermissionHook extends Permission { if (isPgoCheckMemberOf()) { String finalWorld = world; - List localGroups = user.mergePermissionsToList().stream() + List localGroups = user.getOwnNodes().stream() .filter(Node::isGroupNode) .filter(n -> n.shouldApplyOnWorld(finalWorld, isIncludeGlobal(), true)) .filter(n -> n.shouldApplyOnServer(getServer(), isIncludeGlobal(), true)) diff --git a/common/src/main/java/me/lucko/luckperms/common/api/delegates/GroupDelegate.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/GroupDelegate.java index 9a6bf145..17d8514a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/GroupDelegate.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/GroupDelegate.java @@ -153,7 +153,7 @@ public final class GroupDelegate extends PermissionHolderDelegate implements Gro @Override public List getGroupNames() { - return handle.mergePermissionsToList().stream() + return handle.getOwnNodes().stream() .filter(Node::isGroupNode) .map(Node::getGroupName) .collect(Collectors.toList()); @@ -161,7 +161,7 @@ public final class GroupDelegate extends PermissionHolderDelegate implements Gro @Override public List getLocalGroups(@NonNull String server, @NonNull String world) { - return handle.mergePermissionsToList().stream() + return handle.getOwnNodes().stream() .filter(Node::isGroupNode) .filter(n -> n.shouldApplyOnWorld(world, false, true)) .filter(n -> n.shouldApplyOnServer(server, false, true)) @@ -176,7 +176,7 @@ public final class GroupDelegate extends PermissionHolderDelegate implements Gro @Override public List getLocalGroups(@NonNull String server) { - return handle.mergePermissionsToList().stream() + return handle.getOwnNodes().stream() .filter(Node::isGroupNode) .filter(n -> n.shouldApplyOnServer(server, false, true)) .map(Node::getGroupName) diff --git a/common/src/main/java/me/lucko/luckperms/common/api/delegates/PermissionHolderDelegate.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/PermissionHolderDelegate.java index 3afe7604..69927ba5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/PermissionHolderDelegate.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/PermissionHolderDelegate.java @@ -74,12 +74,12 @@ public class PermissionHolderDelegate implements PermissionHolder { @Override public SortedSet getPermissions() { - return ImmutableSortedSet.copyOfSorted(handle.mergePermissionsToSortedSet()); + return ImmutableSortedSet.copyOfSorted(handle.getOwnNodesSorted()); } @Override public Set getEnduringPermissions() { - return ImmutableSet.copyOf(handle.getNodes().values()); + return ImmutableSet.copyOf(handle.getEnduringNodes().values()); } @Override @@ -99,7 +99,7 @@ public class PermissionHolderDelegate implements PermissionHolder { @Override public Map exportNodes(Contexts contexts, boolean lowerCase) { - return new HashMap<>(handle.exportNodes(ExtractedContexts.generate(contexts), lowerCase)); + return new HashMap<>(handle.exportNodesAndShorthand(ExtractedContexts.generate(contexts), lowerCase)); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/api/delegates/UserDelegate.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/UserDelegate.java index 16fc2623..3c736e39 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/UserDelegate.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/UserDelegate.java @@ -198,7 +198,7 @@ public final class UserDelegate extends PermissionHolderDelegate implements User @Override public List getGroupNames() { - return handle.mergePermissionsToList().stream() + return handle.getOwnNodes().stream() .filter(Node::isGroupNode) .map(Node::getGroupName) .collect(Collectors.toList()); @@ -206,7 +206,7 @@ public final class UserDelegate extends PermissionHolderDelegate implements User @Override public List getLocalGroups(@NonNull String server, @NonNull String world) { - return handle.mergePermissionsToList().stream() + return handle.getOwnNodes().stream() .filter(Node::isGroupNode) .filter(n -> n.shouldApplyOnWorld(world, false, true)) .filter(n -> n.shouldApplyOnServer(server, false, true)) @@ -216,7 +216,7 @@ public final class UserDelegate extends PermissionHolderDelegate implements User @Override public List getLocalGroups(@NonNull String server) { - return handle.mergePermissionsToList().stream() + return handle.getOwnNodes().stream() .filter(Node::isGroupNode) .filter(n -> n.shouldApplyOnServer(server, false, true)) .map(Node::getGroupName) diff --git a/common/src/main/java/me/lucko/luckperms/common/buffers/Cache.java b/common/src/main/java/me/lucko/luckperms/common/buffers/Cache.java index 18c83386..82e2e396 100644 --- a/common/src/main/java/me/lucko/luckperms/common/buffers/Cache.java +++ b/common/src/main/java/me/lucko/luckperms/common/buffers/Cache.java @@ -29,7 +29,6 @@ import lombok.RequiredArgsConstructor; import java.util.Optional; import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.function.Supplier; /** * Thread-safe caching utility @@ -37,37 +36,43 @@ import java.util.function.Supplier; * @param the type being stored */ @RequiredArgsConstructor -public class Cache { +public abstract class Cache { private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - private final Supplier supplier; private T cached = null; - public T get() { + protected abstract T supply(); + + public final T get() { + // try to just read from the cached value lock.readLock().lock(); try { if (cached != null) { return cached; } } finally { + // we have to release the read lock, as it is not possible + // to acquire the write lock whilst holding a read lock lock.readLock().unlock(); } lock.writeLock().lock(); try { - // Check again + // Since the lock was unlocked momentarily, we need + // to check again for a cached value if (cached != null) { return cached; } - cached = supplier.get(); + // call the supplier and set the cached value + cached = supply(); return cached; } finally { lock.writeLock().unlock(); } } - public Optional getIfPresent() { + public final Optional getIfPresent() { lock.readLock().lock(); try { return Optional.ofNullable(cached); @@ -76,7 +81,7 @@ public class Cache { } } - public void invalidate() { + public final void invalidate() { lock.writeLock().lock(); try { cached = null; diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java b/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java index 8181a428..8d908de8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java @@ -61,33 +61,11 @@ public class UserCache implements UserData { private final LoadingCache permission = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.MINUTES) - .build(new CacheLoader() { - @Override - public PermissionCache load(Contexts contexts) { - return calculatePermissions(contexts); - } - - @Override - public PermissionCache reload(Contexts contexts, PermissionCache oldData) { - oldData.comparePermissions(user.exportNodes(ExtractedContexts.generate(contexts), true)); - return oldData; - } - }); + .build(new PermissionCacheLoader()); private final LoadingCache meta = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.MINUTES) - .build(new CacheLoader() { - @Override - public MetaCache load(MetaContexts contexts) { - return calculateMeta(contexts); - } - - @Override - public MetaCache reload(MetaContexts contexts, MetaCache oldData) { - oldData.loadMeta(user.accumulateMeta(newAccumulator(contexts), null, ExtractedContexts.generate(contexts.getContexts()))); - return oldData; - } - }); + .build(new MetaCacheLoader()); @Override public PermissionData getPermissionData(@NonNull Contexts contexts) { @@ -108,7 +86,7 @@ public class UserCache implements UserData { @Override public PermissionCache calculatePermissions(@NonNull Contexts contexts) { PermissionCache data = new PermissionCache(contexts, user, user.getPlugin().getCalculatorFactory()); - data.setPermissions(user.exportNodes(ExtractedContexts.generate(contexts), true)); + data.setPermissions(user.exportNodesAndShorthand(ExtractedContexts.generate(contexts), true)); return data; } @@ -183,6 +161,32 @@ public class UserCache implements UserData { meta.invalidateAll(); } + private final class PermissionCacheLoader implements CacheLoader { + @Override + public PermissionCache load(Contexts contexts) { + return calculatePermissions(contexts); + } + + @Override + public PermissionCache reload(Contexts contexts, PermissionCache oldData) { + oldData.comparePermissions(user.exportNodesAndShorthand(ExtractedContexts.generate(contexts), true)); + return oldData; + } + } + + private final class MetaCacheLoader implements CacheLoader { + @Override + public MetaCache load(MetaContexts contexts) { + return calculateMeta(contexts); + } + + @Override + public MetaCache reload(MetaContexts contexts, MetaCache oldData) { + oldData.loadMeta(user.accumulateMeta(newAccumulator(contexts), null, ExtractedContexts.generate(contexts.getContexts()))); + return oldData; + } + } + private static MetaContexts makeFromMetaContextsConfig(Contexts contexts, LuckPermsPlugin plugin) { return new MetaContexts( contexts, diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/meta/MetaClear.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/meta/MetaClear.java index 31d5536d..90725673 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/meta/MetaClear.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/meta/MetaClear.java @@ -57,7 +57,7 @@ public class MetaClear extends SharedSubCommand { return CommandResult.NO_PERMISSION; } - int before = holder.getNodes().size(); + int before = holder.getEnduringNodes().size(); MutableContextSet context = ArgumentUtils.handleContext(0, args, plugin); @@ -72,7 +72,7 @@ public class MetaClear extends SharedSubCommand { holder.clearMeta(context); } - int changed = before - holder.getNodes().size(); + int changed = before - holder.getEnduringNodes().size(); if (changed == 1) { Message.META_CLEAR_SUCCESS_SINGULAR.send(sender, holder.getFriendlyName(), Util.contextSetToString(context), changed); } else { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderClear.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderClear.java index d70c3582..72168443 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderClear.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderClear.java @@ -59,7 +59,7 @@ public class HolderClear extends SubCommand { return CommandResult.NO_PERMISSION; } - int before = holder.getNodes().size(); + int before = holder.getEnduringNodes().size(); MutableContextSet context = ArgumentUtils.handleContext(0, args, plugin); @@ -74,7 +74,7 @@ public class HolderClear extends SubCommand { holder.clearNodes(context); } - int changed = before - holder.getNodes().size(); + int changed = before - holder.getEnduringNodes().size(); if (changed == 1) { Message.CLEAR_SUCCESS_SINGULAR.send(sender, holder.getFriendlyName(), Util.contextSetToString(context), changed); } else { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java index 0b284d3a..384ba1a5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java @@ -83,7 +83,7 @@ public class HolderEditor extends SubCommand { } JsonObject data = new JsonObject(); - Set nodes = holder.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); + Set nodes = holder.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); data.add("nodes", serializePermissions(nodes)); data.addProperty("who", id(holder)); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderShowTracks.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderShowTracks.java index b32d6400..74d9d7f2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderShowTracks.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderShowTracks.java @@ -61,7 +61,7 @@ public class HolderShowTracks extends SubCommand return CommandResult.LOADING_ERROR; } - Set nodes = holder.getNodes().values().stream() + Set nodes = holder.getEnduringNodes().values().stream() .filter(Node::isGroupNode) .filter(Node::isPermanent) .collect(Collectors.toSet()); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClear.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClear.java index ad6224d2..c5ed2df4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClear.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClear.java @@ -57,7 +57,7 @@ public class ParentClear extends SharedSubCommand { return CommandResult.NO_PERMISSION; } - int before = holder.getNodes().size(); + int before = holder.getEnduringNodes().size(); MutableContextSet context = ArgumentUtils.handleContext(0, args, plugin); @@ -72,7 +72,7 @@ public class ParentClear extends SharedSubCommand { holder.clearParents(context, true); } - int changed = before - holder.getNodes().size(); + int changed = before - holder.getEnduringNodes().size(); if (changed == 1) { Message.PARENT_CLEAR_SUCCESS_SINGULAR.send(sender, holder.getFriendlyName(), Util.contextSetToString(context), changed); } else { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClearTrack.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClearTrack.java index a02adf42..2819a417 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClearTrack.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClearTrack.java @@ -84,7 +84,7 @@ public class ParentClearTrack extends SharedSubCommand { return CommandResult.STATE_ERROR; } - int before = holder.getNodes().size(); + int before = holder.getEnduringNodes().size(); MutableContextSet context = ArgumentUtils.handleContext(1, args, plugin); @@ -108,7 +108,7 @@ public class ParentClearTrack extends SharedSubCommand { plugin.getUserManager().giveDefaultIfNeeded(((User) holder), false); } - int changed = before - holder.getNodes().size(); + int changed = before - holder.getEnduringNodes().size(); if (changed == 1) { Message.PARENT_CLEAR_TRACK_SUCCESS_SINGULAR.send(sender, holder.getFriendlyName(), track.getName(), Util.contextSetToString(context), changed); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java index 3de52f75..8a0720dd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java @@ -69,11 +69,11 @@ public class ParentInfo extends SharedSubCommand { return CommandResult.NO_PERMISSION; } - Component ent = permGroupsToMessage(holder.mergePermissionsToSortedSet(), holder, label); + Component ent = permGroupsToMessage(holder.getOwnNodesSorted(), holder, label); Message.LISTNODES.send(sender, holder.getFriendlyName()); sender.sendMessage(ent); - Component tempEnt = tempGroupsToMessage(holder.mergePermissionsToSortedSet(), holder, label); + Component tempEnt = tempGroupsToMessage(holder.getOwnNodesSorted(), holder, label); Message.LISTNODES_TEMP.send(sender, holder.getFriendlyName()); sender.sendMessage(tempEnt); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java index 25bdf429..e6aa3622 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java @@ -88,7 +88,7 @@ public class PermissionInfo extends SharedSubCommand { int page = ArgumentUtils.handleIntOrElse(0, args, 1); - Map.Entry ent = nodesToMessage(false, filter, holder.mergePermissionsToSortedSet(), holder, label, page, sender.isConsole()); + Map.Entry ent = nodesToMessage(false, filter, holder.getOwnNodesSorted(), holder, label, page, sender.isConsole()); if (ent.getValue() != null) { Message.LISTNODES_WITH_PAGE.send(sender, holder.getFriendlyName(), ent.getValue()); sender.sendMessage(ent.getKey()); @@ -97,7 +97,7 @@ public class PermissionInfo extends SharedSubCommand { sender.sendMessage(ent.getKey()); } - Map.Entry tempEnt = nodesToMessage(true, filter, holder.mergePermissionsToSortedSet(), holder, label, page, sender.isConsole()); + Map.Entry tempEnt = nodesToMessage(true, filter, holder.getOwnNodesSorted(), holder, label, page, sender.isConsole()); if (tempEnt.getValue() != null) { Message.LISTNODES_TEMP_WITH_PAGE.send(sender, holder.getFriendlyName(), tempEnt.getValue()); sender.sendMessage(tempEnt.getKey()); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupClone.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupClone.java index 4396adb8..19cb78b8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupClone.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupClone.java @@ -74,7 +74,7 @@ public class GroupClone extends SubCommand { return CommandResult.NO_PERMISSION; } - newGroup.replaceNodes(group.getNodes()); + newGroup.replaceEnduringNodes(group.getEnduringNodes()); Message.CLONE_SUCCESS.send(sender, group.getName(), newGroup.getName()); LogEntry.build().actor(sender).acted(group).action("clone " + newGroup.getName()).build().submit(plugin, sender); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupInfo.java index e24aa6d1..a62cc06a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupInfo.java @@ -68,12 +68,12 @@ public class GroupInfo extends SubCommand { group.getMetaNodes().size() ); - Set parents = group.mergePermissions().stream() + Set parents = group.getOwnNodesSet().stream() .filter(Node::isGroupNode) .filter(Node::isPermanent) .collect(Collectors.toSet()); - Set tempParents = group.mergePermissions().stream() + Set tempParents = group.getOwnNodesSet().stream() .filter(Node::isGroupNode) .filter(Node::isTemporary) .collect(Collectors.toSet()); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupRename.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupRename.java index c017176e..c4315a63 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupRename.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupRename.java @@ -77,7 +77,7 @@ public class GroupRename extends SubCommand { return CommandResult.FAILURE; } - newGroup.replaceNodes(group.getNodes()); + newGroup.replaceEnduringNodes(group.getEnduringNodes()); Message.RENAME_SUCCESS.send(sender, group.getName(), newGroup.getName()); LogEntry.build().actor(sender).acted(group).action("rename " + newGroup.getName()).build().submit(plugin, sender); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/ApplyEditsCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/ApplyEditsCommand.java index 9f77cf33..10d4e896 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/ApplyEditsCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/ApplyEditsCommand.java @@ -141,7 +141,7 @@ public class ApplyEditsCommand extends SingleCommand { } Set nodes = deserializePermissions(data.getAsJsonArray("nodes")); - holder.setNodes(nodes.stream().map(NodeModel::toNode).collect(Collectors.toSet())); + holder.setEnduringNodes(nodes.stream().map(NodeModel::toNode).collect(Collectors.toSet())); Message.APPLY_EDITS_SUCCESS.send(sender, nodes.size(), holder.getFriendlyName()); SharedSubCommand.save(holder, sender, plugin); return CommandResult.SUCCESS; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java index d7acc8b0..212c7f04 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java @@ -97,7 +97,7 @@ public class UserDemote extends SubCommand { } // Load applicable groups - Set nodes = user.getNodes().values().stream() + Set nodes = user.getEnduringNodes().values().stream() .filter(Node::isGroupNode) .filter(Node::getValue) .filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable())) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java index 1fdf94c3..93d0c3b0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java @@ -72,12 +72,12 @@ public class UserInfo extends SubCommand { user.getMetaNodes().size() ); - Set parents = user.mergePermissions().stream() + Set parents = user.getOwnNodesSet().stream() .filter(Node::isGroupNode) .filter(Node::isPermanent) .collect(Collectors.toSet()); - Set tempParents = user.mergePermissions().stream() + Set tempParents = user.getOwnNodesSet().stream() .filter(Node::isGroupNode) .filter(Node::isTemporary) .collect(Collectors.toSet()); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java index 4cb33f5c..e0a0a4ef 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java @@ -95,7 +95,7 @@ public class UserPromote extends SubCommand { } // Load applicable groups - Set nodes = user.getNodes().values().stream() + Set nodes = user.getEnduringNodes().values().stream() .filter(Node::isGroupNode) .filter(Node::getValue) .filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable())) diff --git a/common/src/main/java/me/lucko/luckperms/common/data/Exporter.java b/common/src/main/java/me/lucko/luckperms/common/data/Exporter.java index 077b09a7..1525b278 100644 --- a/common/src/main/java/me/lucko/luckperms/common/data/Exporter.java +++ b/common/src/main/java/me/lucko/luckperms/common/data/Exporter.java @@ -131,7 +131,7 @@ public class Exporter implements Runnable { }) .forEach(group -> { write(writer, "# Export group: " + group.getName()); - for (Node node : group.getNodes().values()) { + for (Node node : group.getEnduringNodes().values()) { write(writer, NodeFactory.nodeAsCommand(node, group.getName(), true, true)); } write(writer, ""); @@ -234,7 +234,7 @@ public class Exporter implements Runnable { output.add("# Export user: " + user.getUuid().toString() + " - " + user.getName().orElse("unknown username")); boolean inDefault = false; - for (Node node : user.getNodes().values()) { + for (Node node : user.getEnduringNodes().values()) { if (node.isGroupNode() && node.getGroupName().equalsIgnoreCase("default")) { inDefault = true; continue; diff --git a/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java b/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java index 8bd3a6de..df0fb6e9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java @@ -67,8 +67,8 @@ import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.User; +import java.util.Collection; import java.util.List; -import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; @@ -86,7 +86,7 @@ public final class EventFactory { } public void handleGroupDelete(Group group, DeletionCause cause) { - EventGroupDelete event = new EventGroupDelete(group.getName(), ImmutableSet.copyOf(group.getNodes().values()), cause); + EventGroupDelete event = new EventGroupDelete(group.getName(), ImmutableSet.copyOf(group.getEnduringNodes().values()), cause); fireEvent(event); } @@ -114,17 +114,17 @@ public final class EventFactory { return cancel.get(); } - public void handleNodeAdd(Node node, PermissionHolder target, Set before, Set after) { + public void handleNodeAdd(Node node, PermissionHolder target, Collection before, Collection after) { EventNodeAdd event = new EventNodeAdd(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); fireEvent(event); } - public void handleNodeClear(PermissionHolder target, Set before, Set after) { + public void handleNodeClear(PermissionHolder target, Collection before, Collection after) { EventNodeClear event = new EventNodeClear(target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); fireEvent(event); } - public void handleNodeRemove(Node node, PermissionHolder target, Set before, Set after) { + public void handleNodeRemove(Node node, PermissionHolder target, Collection before, Collection after) { EventNodeRemove event = new EventNodeRemove(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); fireEvent(event); } diff --git a/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java b/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java index ec27b6d9..b96fcf92 100644 --- a/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java @@ -43,7 +43,7 @@ public class GenericUserManager extends AbstractManager im boolean hasGroup = false; if (user.getPrimaryGroup().getStoredValue() != null && !user.getPrimaryGroup().getStoredValue().isEmpty()) { - for (Node node : user.getNodes().values()) { + for (Node node : user.getEnduringNodes().values()) { if (node.hasSpecificContext()) { continue; } @@ -85,11 +85,11 @@ public class GenericUserManager extends AbstractManager im * @return true if the user should be saved */ public static boolean shouldSave(User user) { - if (user.getNodes().size() != 1) { + if (user.getEnduringNodes().size() != 1) { return true; } - for (Node node : user.getNodes().values()) { + for (Node node : user.getEnduringNodes().values()) { // There's only one. if (!node.isGroupNode()) { return true; diff --git a/common/src/main/java/me/lucko/luckperms/common/model/Group.java b/common/src/main/java/me/lucko/luckperms/common/model/Group.java index c8014eeb..522fc3a9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/Group.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/Group.java @@ -50,12 +50,12 @@ public class Group extends PermissionHolder implements Identifiable { public Group(String name, LuckPermsPlugin plugin) { super(name, plugin); - this.name = name; + this.name = name.toLowerCase(); } @Override public String getId() { - return name.toLowerCase(); + return name; } public String getRawDisplayName() { 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 10b0723d..a8b534c2 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 @@ -29,6 +29,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; @@ -45,6 +46,7 @@ import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.api.delegates.PermissionHolderDelegate; +import me.lucko.luckperms.common.buffers.Cache; import me.lucko.luckperms.common.caching.MetaAccumulator; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.contexts.ContextSetComparator; @@ -53,19 +55,19 @@ import me.lucko.luckperms.common.node.ImmutableLocalizedNode; import me.lucko.luckperms.common.node.InheritanceInfo; import me.lucko.luckperms.common.node.NodeComparator; import me.lucko.luckperms.common.node.NodeFactory; -import me.lucko.luckperms.common.node.NodePriorityComparator; 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.references.GroupReference; import me.lucko.luckperms.common.references.HolderReference; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -129,6 +131,25 @@ public abstract class PermissionHolder { .treeSetValues(NodeComparator.reverse()) .build(); + /** + * Caches an immutable copy of the above nodes multimap + */ + private final class NodesCache extends Cache> { + @Override + protected ImmutableSetMultimap supply() { + nodesLock.lock(); + try { + return ImmutableSetMultimap.copyOf(nodes); + } finally { + nodesLock.unlock(); + } + } + } + private final NodesCache nodesCopy = new NodesCache(); + + // used to ensure thread safe access to the backing nodes map + private final ReentrantLock nodesLock = new ReentrantLock(); + /** * The holders transient nodes. * @@ -146,6 +167,25 @@ public abstract class PermissionHolder { .treeSetValues(NodeComparator.reverse()) .build(); + /** + * Caches an immutable copy of the above transientNodes multimap + */ + private final class TransientNodesCache extends Cache> { + @Override + protected ImmutableSetMultimap supply() { + transientNodesLock.lock(); + try { + return ImmutableSetMultimap.copyOf(transientNodes); + } finally { + transientNodesLock.unlock(); + } + } + } + private final TransientNodesCache transientNodesCopy = new TransientNodesCache(); + + // used to ensure thread safe access to the backing transientNodes map + private final ReentrantLock transientNodesLock = new ReentrantLock(); + /** * Lock used by Storage implementations to prevent concurrent read/writes */ @@ -159,6 +199,9 @@ public abstract class PermissionHolder { private final Set stateListeners = ConcurrentHashMap.newKeySet(); private void invalidateCache() { + nodesCopy.invalidate(); + transientNodesCopy.invalidate(); + // Invalidate listeners for (Runnable r : stateListeners) { try { @@ -206,10 +249,8 @@ public abstract class PermissionHolder { * * @return an immutable copy of the multimap storing this objects nodes */ - public ImmutableSetMultimap getNodes() { - synchronized (nodes) { - return ImmutableSetMultimap.copyOf(nodes); - } + public ImmutableSetMultimap getEnduringNodes() { + return nodesCopy.get(); } /** @@ -218,9 +259,7 @@ public abstract class PermissionHolder { * @return an immutable copy of the multimap storing this objects transient nodes */ public ImmutableSetMultimap getTransientNodes() { - synchronized (transientNodes) { - return ImmutableSetMultimap.copyOf(transientNodes); - } + return transientNodesCopy.get(); } /** @@ -228,12 +267,15 @@ public abstract class PermissionHolder { * * @param set the set of nodes to apply to the object */ - public void setNodes(Set set) { - synchronized (nodes) { + public void setEnduringNodes(Set set) { + nodesLock.lock(); + try { nodes.clear(); for (Node n : set) { nodes.put(n.getFullContexts().makeImmutable(), n); } + } finally { + nodesLock.unlock(); } invalidateCache(); } @@ -243,28 +285,37 @@ public abstract class PermissionHolder { * * @param multimap the replacement multimap */ - public void replaceNodes(Multimap multimap) { - synchronized (nodes) { + public void replaceEnduringNodes(Multimap multimap) { + nodesLock.lock(); + try { nodes.clear(); nodes.putAll(multimap); + } finally { + nodesLock.unlock(); } invalidateCache(); } public void setTransientNodes(Set set) { - synchronized (transientNodes) { + transientNodesLock.lock(); + try { transientNodes.clear(); for (Node n : set) { transientNodes.put(n.getFullContexts().makeImmutable(), n); } + } finally { + transientNodesLock.unlock(); } invalidateCache(); } public void replaceTransientNodes(Multimap multimap) { - synchronized (transientNodes) { + transientNodesLock.lock(); + try { transientNodes.clear(); transientNodes.putAll(multimap); + } finally { + transientNodesLock.unlock(); } invalidateCache(); } @@ -274,161 +325,147 @@ public abstract class PermissionHolder { * * @return a set containing the holders enduring and transient permissions */ - public LinkedHashSet mergePermissions() { + public LinkedHashSet getOwnNodesSet() { LinkedHashSet ret = new LinkedHashSet<>(); - synchronized (transientNodes) { + + transientNodesLock.lock(); + try { ret.addAll(transientNodes.values()); + } finally { + transientNodesLock.unlock(); } - synchronized (nodes) { + nodesLock.lock(); + try { ret.addAll(nodes.values()); + } finally { + nodesLock.unlock(); } return ret; } - public List mergePermissionsToList() { + public List getOwnNodes() { List ret = new ArrayList<>(); - synchronized (transientNodes) { + + transientNodesLock.lock(); + try { ret.addAll(transientNodes.values()); + } finally { + transientNodesLock.unlock(); } - synchronized (nodes) { + + nodesLock.lock(); + try { ret.addAll(nodes.values()); + } finally { + nodesLock.unlock(); } return ret; } - public SortedSet mergePermissionsToSortedSet() { - SortedSet ret = new TreeSet<>(NodePriorityComparator.reverse()); - ret.addAll(mergePermissions().stream().map(n -> ImmutableLocalizedNode.of(n, getObjectName())).collect(Collectors.toSet())); + public SortedSet getOwnNodesSorted() { + SortedSet ret = new TreeSet<>(NodeWithContextComparator.reverse()); + + transientNodesLock.lock(); + try { + for (Node node : transientNodes.values()) { + ret.add(ImmutableLocalizedNode.of(node, getObjectName())); + } + } finally { + transientNodesLock.unlock(); + } + + nodesLock.lock(); + try { + for (Node node : nodes.values()) { + ret.add(ImmutableLocalizedNode.of(node, getObjectName())); + } + } finally { + nodesLock.unlock(); + } + return ret; } - public LinkedHashSet flattenNodes() { - synchronized (nodes) { - return new LinkedHashSet<>(nodes.values()); - } + public List filterEnduringNodes(ContextSet filter) { + return filterEnduringNodes(new ArrayList<>(), filter); } - public LinkedHashSet flattenNodes(ContextSet filter) { - synchronized (nodes) { - LinkedHashSet set = new LinkedHashSet<>(); + public > C filterEnduringNodes(C ret, ContextSet filter) { + nodesLock.lock(); + try { for (Map.Entry> e : nodes.asMap().entrySet()) { if (e.getKey().isSatisfiedBy(filter)) { - set.addAll(e.getValue()); + ret.addAll(e.getValue()); } } - - return set; + } finally { + nodesLock.unlock(); } + + return ret; } - public LinkedHashSet flattenTransientNodes() { - synchronized (transientNodes) { - return new LinkedHashSet<>(transientNodes.values()); - } + public List filterTransientNodes(ContextSet filter) { + return filterTransientNodes(new ArrayList<>(), filter); } - public LinkedHashSet flattenTransientNodes(ContextSet filter) { - synchronized (transientNodes) { - LinkedHashSet set = new LinkedHashSet<>(); + public > C filterTransientNodes(C ret, ContextSet filter) { + transientNodesLock.lock(); + try { for (Map.Entry> e : transientNodes.asMap().entrySet()) { if (e.getKey().isSatisfiedBy(filter)) { - set.addAll(e.getValue()); + ret.addAll(e.getValue()); } } - - return set; + } finally { + transientNodesLock.unlock(); } + + return ret; } - public LinkedHashSet flattenAndMergeNodes(ContextSet filter) { - LinkedHashSet set = new LinkedHashSet<>(); - synchronized (transientNodes) { + public List filterNodes(ContextSet filter) { + return filterNodes(new ArrayList<>(), filter); + } + + public > C filterNodes(C ret, ContextSet filter) { + transientNodesLock.lock(); + try { for (Map.Entry> e : transientNodes.asMap().entrySet()) { if (e.getKey().isSatisfiedBy(filter)) { - set.addAll(e.getValue()); + ret.addAll(e.getValue()); } } + } finally { + transientNodesLock.unlock(); } - synchronized (nodes) { + + nodesLock.lock(); + try { for (Map.Entry> e : nodes.asMap().entrySet()) { if (e.getKey().isSatisfiedBy(filter)) { - set.addAll(e.getValue()); + ret.addAll(e.getValue()); } } + } finally { + nodesLock.unlock(); } - return set; - } - public List flattenNodesToList() { - synchronized (nodes) { - return new ArrayList<>(nodes.values()); - } - } - - public List flattenNodesToList(ContextSet filter) { - synchronized (nodes) { - List set = new ArrayList<>(); - for (Map.Entry> e : nodes.asMap().entrySet()) { - if (e.getKey().isSatisfiedBy(filter)) { - set.addAll(e.getValue()); - } - } - - return set; - } - } - - public List flattenTransientNodesToList() { - synchronized (transientNodes) { - return new ArrayList<>(transientNodes.values()); - } - } - - public List flattenTransientNodesToList(ContextSet filter) { - synchronized (transientNodes) { - if (transientNodes.isEmpty()) { - return Collections.emptyList(); - } - - List set = new ArrayList<>(); - for (Map.Entry> e : transientNodes.asMap().entrySet()) { - if (e.getKey().isSatisfiedBy(filter)) { - set.addAll(e.getValue()); - } - } - - return set; - } - } - - public List flattenAndMergeNodesToList(ContextSet filter) { - List set = new ArrayList<>(); - synchronized (transientNodes) { - for (Map.Entry> e : transientNodes.asMap().entrySet()) { - if (e.getKey().isSatisfiedBy(filter)) { - set.addAll(e.getValue()); - } - } - } - synchronized (nodes) { - for (Map.Entry> e : nodes.asMap().entrySet()) { - if (e.getKey().isSatisfiedBy(filter)) { - set.addAll(e.getValue()); - } - } - } - return set; + return ret; } public boolean removeIf(Predicate predicate) { boolean result; - ImmutableSet before = ImmutableSet.copyOf(flattenNodes()); + ImmutableCollection before = getEnduringNodes().values();; - synchronized (nodes) { + nodesLock.lock(); + try { result = nodes.values().removeIf(predicate); + } finally { + nodesLock.unlock(); } if (!result) { @@ -436,7 +473,8 @@ public abstract class PermissionHolder { } invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(flattenNodes()); + + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeClear(this, before, after); return true; } @@ -444,8 +482,11 @@ public abstract class PermissionHolder { public boolean removeIfTransient(Predicate predicate) { boolean result; - synchronized (nodes) { + transientNodesLock.lock(); + try { result = transientNodes.values().removeIf(predicate); + } finally { + transientNodesLock.unlock(); } if (result) { @@ -476,37 +517,42 @@ public abstract class PermissionHolder { } // get and add the objects own nodes - List nodes = flattenAndMergeNodesToList(context.getContextSet()); - nodes.stream() - .map(n -> ImmutableLocalizedNode.of(n, getObjectName())) - .forEach(accumulator::add); + List nodes = filterNodes(context.getContextSet()); + for (Node node : nodes) { + ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, getObjectName()); + accumulator.add(localizedNode); + } Contexts contexts = context.getContexts(); - // screw effectively final - Set finalExcludedGroups = excludedGroups; - List finalAccumulator = accumulator; + // resolve and process the objects parents + List resolvedGroups = new ArrayList<>(); + Set processedGroups = new HashSet<>(); - // this allows you to negate parent permissions lower down the inheritance tree. - // we can negate parent groups in a specific context at this level and prevent them from being applied. - // there's no way to distinct the stream below based on a custom comparator. - NodeTools.removeSamePermission(nodes.iterator()); + for (Node n : nodes) { + if (!n.isGroupNode()) continue; + String groupName = n.getGroupName(); - nodes.stream() - .filter(Node::getValue) - .filter(Node::isGroupNode) - .filter(n -> !(!contexts.isApplyGlobalGroups() && !n.isServerSpecific()) && !(!contexts.isApplyGlobalWorldGroups() && !n.isWorldSpecific())) - .map(Node::getGroupName) - .distinct() - .map(n -> Optional.ofNullable(plugin.getGroupManager().getIfLoaded(n))) - .filter(Optional::isPresent) - .map(Optional::get) - .filter(g -> !finalExcludedGroups.contains(g.getObjectName().toLowerCase())) - .sorted((o1, o2) -> { - int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); - return result == 1 ? -1 : 1; - }) - .forEach(group -> group.resolveInheritances(finalAccumulator, finalExcludedGroups, context)); + if (!processedGroups.add(groupName) || excludedGroups.contains(groupName)) continue; + + if (!((contexts.isApplyGlobalGroups() || n.isServerSpecific()) && (contexts.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) { + continue; + } + + Group g = plugin.getGroupManager().getIfLoaded(groupName); + if (g != null) { + resolvedGroups.add(g); + } + } + + resolvedGroups.sort((o1, o2) -> { + int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); + return result == 1 ? -1 : 1; + }); + + for (Group g : resolvedGroups) { + g.resolveInheritances(accumulator, excludedGroups, context); + } return accumulator; } @@ -516,17 +562,17 @@ public abstract class PermissionHolder { } public SortedSet resolveInheritancesAlmostEqual(ExtractedContexts contexts) { - List nodes = resolveInheritances(null, null, contexts); + List nodes = resolveInheritances(new LinkedList<>(), null, contexts); NodeTools.removeAlmostEqual(nodes.iterator()); - SortedSet ret = new TreeSet<>(NodePriorityComparator.reverse()); + SortedSet ret = new TreeSet<>(NodeWithContextComparator.reverse()); ret.addAll(nodes); return ret; } public SortedSet resolveInheritancesMergeTemp(ExtractedContexts contexts) { - List nodes = resolveInheritances(null, null, contexts); + List nodes = resolveInheritances(new LinkedList<>(), null, contexts); NodeTools.removeIgnoreValueOrTemp(nodes.iterator()); - SortedSet ret = new TreeSet<>(NodePriorityComparator.reverse()); + SortedSet ret = new TreeSet<>(NodeWithContextComparator.reverse()); ret.addAll(nodes); return ret; } @@ -551,34 +597,36 @@ public abstract class PermissionHolder { } // get and add the objects own nodes - List nodes = mergePermissionsToList(); - nodes.stream() - .map(n -> ImmutableLocalizedNode.of(n, getObjectName())) - .forEach(accumulator::add); + List nodes = getOwnNodes(); + for (Node node : nodes) { + ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, getObjectName()); + accumulator.add(localizedNode); + } - // screw effectively final - Set finalExcludedGroups = excludedGroups; - List finalAccumulator = accumulator; + // resolve and process the objects parents + List resolvedGroups = new ArrayList<>(); + Set processedGroups = new HashSet<>(); - // this allows you to negate parent permissions lower down the inheritance tree. - // we can negate parent groups in a specific context at this level and prevent them from being applied. - // there's no way to distinct the stream below based on a custom comparator. - NodeTools.removeSamePermission(nodes.iterator()); + for (Node n : nodes) { + if (!n.isGroupNode()) continue; + String groupName = n.getGroupName(); - nodes.stream() - .filter(Node::getValue) - .filter(Node::isGroupNode) - .map(Node::getGroupName) - .distinct() - .map(n -> Optional.ofNullable(plugin.getGroupManager().getIfLoaded(n))) - .filter(Optional::isPresent) - .map(Optional::get) - .filter(g -> !finalExcludedGroups.contains(g.getObjectName().toLowerCase())) - .sorted((o1, o2) -> { - int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); - return result == 1 ? -1 : 1; - }) - .forEach(group -> group.resolveInheritances(finalAccumulator, finalExcludedGroups)); + if (!processedGroups.add(groupName) || excludedGroups.contains(groupName)) continue; + + Group g = plugin.getGroupManager().getIfLoaded(groupName); + if (g != null) { + resolvedGroups.add(g); + } + } + + resolvedGroups.sort((o1, o2) -> { + int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); + return result == 1 ? -1 : 1; + }); + + for (Group g : resolvedGroups) { + g.resolveInheritances(accumulator, excludedGroups); + } return accumulator; } @@ -588,17 +636,17 @@ public abstract class PermissionHolder { } public SortedSet resolveInheritancesAlmostEqual() { - List nodes = resolveInheritances(null, null); + List nodes = resolveInheritances(new LinkedList<>(), null); NodeTools.removeAlmostEqual(nodes.iterator()); - SortedSet ret = new TreeSet<>(NodePriorityComparator.reverse()); + SortedSet ret = new TreeSet<>(NodeWithContextComparator.reverse()); ret.addAll(nodes); return ret; } public SortedSet resolveInheritancesMergeTemp() { - List nodes = resolveInheritances(null, null); + List nodes = resolveInheritances(new LinkedList<>(), null); NodeTools.removeIgnoreValueOrTemp(nodes.iterator()); - SortedSet ret = new TreeSet<>(NodePriorityComparator.reverse()); + SortedSet ret = new TreeSet<>(NodeWithContextComparator.reverse()); ret.addAll(nodes); return ret; } @@ -608,9 +656,13 @@ public abstract class PermissionHolder { List entries; if (contexts.isApplyGroups()) { - entries = resolveInheritances(null, null, context); + entries = resolveInheritances(new LinkedList<>(), null, context); } else { - entries = flattenNodesToList(context.getContextSet()).stream().map(n -> ImmutableLocalizedNode.of(n, getObjectName())).collect(Collectors.toList()); + entries = new LinkedList<>(); + for (Node n : filterNodes(context.getContextSet())) { + ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName()); + entries.add(localizedNode); + } } if (!contexts.isIncludeGlobal()) { @@ -621,19 +673,23 @@ public abstract class PermissionHolder { } NodeTools.removeSamePermission(entries.iterator()); - SortedSet ret = new TreeSet<>(NodePriorityComparator.reverse()); + SortedSet ret = new TreeSet<>(NodeWithContextComparator.reverse()); ret.addAll(entries); return ret; } - public Map exportNodes(ExtractedContexts context, boolean lowerCase) { + public Map exportNodesAndShorthand(ExtractedContexts context, boolean lowerCase) { Contexts contexts = context.getContexts(); - List entries; + List entries; if (contexts.isApplyGroups()) { - entries = resolveInheritances(null, null, context); + entries = resolveInheritances(new LinkedList<>(), null, context); } else { - entries = flattenNodesToList(context.getContextSet()); + entries = new LinkedList<>(); + for (Node n : filterNodes(context.getContextSet())) { + ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName()); + entries.add(localizedNode); + } } if (!contexts.isIncludeGlobal()) { @@ -644,11 +700,12 @@ public abstract class PermissionHolder { } Map perms = new HashMap<>(); + boolean applyShorthand = plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND); for (Node node : entries) { String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission(); if (perms.putIfAbsent(perm, node.getValue()) == null) { - if (plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND)) { + if (applyShorthand) { List sh = node.resolveShorthand(); if (!sh.isEmpty()) { sh.stream().map(s -> lowerCase ? s.toLowerCase() : s).forEach(s -> perms.putIfAbsent(s, node.getValue())); @@ -675,44 +732,109 @@ public abstract class PermissionHolder { Contexts contexts = context.getContexts(); - // screw effectively final - Set finalExcludedGroups = excludedGroups; - MetaAccumulator finalAccumulator = accumulator; - // get and add the objects own nodes - List nodes = flattenAndMergeNodesToList(context.getContextSet()); + List nodes = filterNodes(context.getContextSet()); - // this allows you to negate parent permissions lower down the inheritance tree. - // it also allows you to negate meta in specific contexts and have it override. - // there's no way to distinct the stream below based on a custom comparator. - NodeTools.removeSamePermission(nodes.iterator()); + for (Node node : nodes) { + if (!node.getValue()) continue; + if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue; - nodes.stream() - .filter(Node::getValue) - .filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix()) - .filter(n -> !(!contexts.isIncludeGlobal() && !n.isServerSpecific()) && !(!contexts.isIncludeGlobalWorld() && !n.isWorldSpecific())) - .forEach(n -> finalAccumulator.accumulateNode(ImmutableLocalizedNode.of(n, getObjectName()))); + if (!((contexts.isIncludeGlobal() || node.isServerSpecific()) && (contexts.isIncludeGlobalWorld() || node.isWorldSpecific()))) { + continue; + } + + accumulator.accumulateNode(ImmutableLocalizedNode.of(node, getObjectName())); + } OptionalInt w = getWeight(); if (w.isPresent()) { accumulator.accumulateWeight(w.getAsInt()); } - nodes.stream() - .filter(Node::getValue) - .filter(Node::isGroupNode) - .filter(n -> !(!contexts.isApplyGlobalGroups() && !n.isServerSpecific()) && !(!contexts.isApplyGlobalWorldGroups() && !n.isWorldSpecific())) - .map(Node::getGroupName) - .distinct() - .map(n -> Optional.ofNullable(plugin.getGroupManager().getIfLoaded(n))) - .filter(Optional::isPresent) - .map(Optional::get) - .filter(g -> !finalExcludedGroups.contains(g.getObjectName().toLowerCase())) - .sorted((o1, o2) -> { - int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); - return result == 1 ? -1 : 1; - }) - .forEach(group -> group.accumulateMeta(finalAccumulator, finalExcludedGroups, context)); + // resolve and process the objects parents + List resolvedGroups = new ArrayList<>(); + Set processedGroups = new HashSet<>(); + + for (Node n : nodes) { + if (!n.isGroupNode()) continue; + String groupName = n.getGroupName(); + + if (!processedGroups.add(groupName) || excludedGroups.contains(groupName)) continue; + + if (!((contexts.isApplyGlobalGroups() || n.isServerSpecific()) && (contexts.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) { + continue; + } + + Group g = plugin.getGroupManager().getIfLoaded(groupName); + if (g != null) { + resolvedGroups.add(g); + } + } + + resolvedGroups.sort((o1, o2) -> { + int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); + return result == 1 ? -1 : 1; + }); + + for (Group g : resolvedGroups) { + g.accumulateMeta(accumulator, excludedGroups, context); + } + + return accumulator; + } + + public MetaAccumulator accumulateMeta(MetaAccumulator accumulator, Set excludedGroups) { + if (accumulator == null) { + accumulator = MetaAccumulator.makeFromConfig(plugin); + } + + if (excludedGroups == null) { + excludedGroups = new HashSet<>(); + } + + if (this instanceof Group) { + excludedGroups.add(getObjectName().toLowerCase()); + } + + // get and add the objects own nodes + List nodes = getOwnNodes(); + + for (Node node : nodes) { + if (!node.getValue()) 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 resolvedGroups = new ArrayList<>(); + Set processedGroups = new HashSet<>(); + + for (Node n : nodes) { + if (!n.isGroupNode()) continue; + String groupName = n.getGroupName(); + + if (!processedGroups.add(groupName) || excludedGroups.contains(groupName)) continue; + + Group g = plugin.getGroupManager().getIfLoaded(groupName); + if (g != null) { + resolvedGroups.add(g); + } + } + + resolvedGroups.sort((o1, o2) -> { + int result = Integer.compare(o1.getWeight().orElse(0), o2.getWeight().orElse(0)); + return result == 1 ? -1 : 1; + }); + + for (Group g : resolvedGroups) { + g.accumulateMeta(accumulator, excludedGroups); + } return accumulator; } @@ -726,9 +848,10 @@ public abstract class PermissionHolder { boolean work = false; Set removed = new HashSet<>(); - ImmutableSet before = ImmutableSet.copyOf(mergePermissions()); + ImmutableSet before = ImmutableSet.copyOf(getOwnNodesSet()); - synchronized (nodes) { + nodesLock.lock(); + try { Iterator it = nodes.values().iterator(); while (it.hasNext()) { Node entry = it.next(); @@ -738,6 +861,8 @@ public abstract class PermissionHolder { it.remove(); } } + } finally { + nodesLock.unlock(); } if (work) { @@ -745,7 +870,8 @@ public abstract class PermissionHolder { work = false; } - synchronized (transientNodes) { + transientNodesLock.lock(); + try { Iterator it = transientNodes.values().iterator(); while (it.hasNext()) { Node entry = it.next(); @@ -755,6 +881,8 @@ public abstract class PermissionHolder { it.remove(); } } + } finally { + transientNodesLock.unlock(); } if (work) { @@ -765,7 +893,7 @@ public abstract class PermissionHolder { return false; } - ImmutableSet after = ImmutableSet.copyOf(mergePermissions()); + ImmutableSet after = ImmutableSet.copyOf(getOwnNodesSet()); for (Node r : removed) { plugin.getApiProvider().getEventFactory().handleNodeRemove(r, this, before, after); @@ -775,7 +903,7 @@ public abstract class PermissionHolder { } public Optional getAlmostEquals(Node node, boolean t) { - for (Node n : t ? getTransientNodes().values() : getNodes().values()) { + for (Node n : t ? getTransientNodes().values() : getEnduringNodes().values()) { if (n.almostEquals(node)) { return Optional.of(n); } @@ -887,14 +1015,17 @@ public abstract class PermissionHolder { return DataMutateResult.ALREADY_HAS; } - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection before = getEnduringNodes().values();; - synchronized (nodes) { + nodesLock.lock(); + try { nodes.put(node.getFullContexts().makeImmutable(), node); + } finally { + nodesLock.unlock(); } invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeAdd(node, this, before, after); return DataMutateResult.SUCCESS; @@ -920,16 +1051,19 @@ public abstract class PermissionHolder { // Create a new node with the same properties, but add the expiry dates together Node newNode = NodeFactory.builderFromExisting(node).setExpiry(previous.getExpiryUnixTime() + node.getSecondsTilExpiry()).build(); - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection before = getEnduringNodes().values(); // Remove the old node & add the new one. - synchronized (nodes) { + nodesLock.lock(); + try { nodes.remove(previous.getFullContexts().makeImmutable(), previous); nodes.put(newNode.getFullContexts().makeImmutable(), newNode); + } finally { + nodesLock.unlock(); } invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeAdd(newNode, this, before, after); return Maps.immutableEntry(DataMutateResult.SUCCESS, newNode); } @@ -945,15 +1079,18 @@ public abstract class PermissionHolder { // Only replace if the new expiry time is greater than the old one. if (node.getExpiryUnixTime() > previous.getExpiryUnixTime()) { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection before = getEnduringNodes().values(); - synchronized (nodes) { + nodesLock.lock(); + try { nodes.remove(previous.getFullContexts().makeImmutable(), previous); nodes.put(node.getFullContexts().makeImmutable(), node); + } finally { + nodesLock.unlock(); } invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeAdd(node, this, before, after); return Maps.immutableEntry(DataMutateResult.SUCCESS, node); } @@ -977,14 +1114,18 @@ public abstract class PermissionHolder { return DataMutateResult.ALREADY_HAS; } - ImmutableSet before = ImmutableSet.copyOf(getTransientNodes().values()); + ImmutableCollection before = getTransientNodes().values(); - synchronized (transientNodes) { + transientNodesLock.lock(); + try { transientNodes.put(node.getFullContexts().makeImmutable(), node); + } finally { + transientNodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getTransientNodes().values()); + ImmutableCollection after = getTransientNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeAdd(node, this, before, after); return DataMutateResult.SUCCESS; @@ -1000,14 +1141,18 @@ public abstract class PermissionHolder { return DataMutateResult.LACKS; } - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection before = getEnduringNodes().values(); - synchronized (nodes) { - this.nodes.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.almostEquals(node)); + nodesLock.lock(); + try { + nodes.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.almostEquals(node)); + } finally { + nodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeRemove(node, this, before, after); return DataMutateResult.SUCCESS; } @@ -1018,14 +1163,18 @@ public abstract class PermissionHolder { * @param node the node to unset */ public DataMutateResult unsetPermissionExact(Node node) { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection before = getEnduringNodes().values(); - synchronized (nodes) { + nodesLock.lock(); + try { nodes.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.equals(node)); + } finally { + nodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); if (before.size() == after.size()) { return DataMutateResult.LACKS; @@ -1045,14 +1194,18 @@ public abstract class PermissionHolder { return DataMutateResult.LACKS; } - ImmutableSet before = ImmutableSet.copyOf(getTransientNodes().values()); + ImmutableCollection before = getTransientNodes().values(); - synchronized (transientNodes) { + transientNodesLock.lock(); + try { transientNodes.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.almostEquals(node)); + } finally { + transientNodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getTransientNodes().values()); + ImmutableCollection after = getTransientNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeRemove(node, this, before, after); return DataMutateResult.SUCCESS; } @@ -1085,12 +1238,17 @@ public abstract class PermissionHolder { * Clear all of the holders permission nodes */ public boolean clearNodes() { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); - synchronized (nodes) { + ImmutableCollection before = getEnduringNodes().values(); + + nodesLock.lock(); + try { nodes.clear(); + } finally { + nodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); if (before.size() == after.size()) { return false; @@ -1101,12 +1259,16 @@ public abstract class PermissionHolder { } public boolean clearNodes(ContextSet contextSet) { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); - synchronized (nodes) { + ImmutableCollection before = getEnduringNodes().values(); + nodesLock.lock(); + try { nodes.removeAll(contextSet.makeImmutable()); + } finally { + nodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); if (before.size() == after.size()) { return false; @@ -1117,25 +1279,33 @@ public abstract class PermissionHolder { } public boolean clearParents(boolean giveDefault) { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); - synchronized (nodes) { + ImmutableCollection before = getEnduringNodes().values(); + + nodesLock.lock(); + try { boolean b = nodes.values().removeIf(Node::isGroupNode); if (!b) { return false; } + } finally { + nodesLock.unlock(); } + if (this instanceof User && giveDefault) { plugin.getUserManager().giveDefaultIfNeeded((User) this, false); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearParents(ContextSet contextSet, boolean giveDefault) { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); - synchronized (nodes) { + ImmutableCollection before = getEnduringNodes().values(); + + nodesLock.lock(); + try { SortedSet nodes = this.nodes.get(contextSet.makeImmutable()); if (nodes == null) { return false; @@ -1145,33 +1315,42 @@ public abstract class PermissionHolder { if (!b) { return false; } + } finally { + nodesLock.unlock(); } + if (this instanceof User && giveDefault) { plugin.getUserManager().giveDefaultIfNeeded((User) this, false); } invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearMeta() { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection before = getEnduringNodes().values(); - synchronized (nodes) { + nodesLock.lock(); + try { if (!nodes.values().removeIf(n -> n.isMeta() || n.isPrefix() || n.isSuffix())) { return false; } + } finally { + nodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearMeta(ContextSet contextSet) { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); - synchronized (nodes) { + ImmutableCollection before = getEnduringNodes().values(); + + nodesLock.lock(); + try { SortedSet nodes = this.nodes.get(contextSet.makeImmutable()); if (nodes == null) { return false; @@ -1181,31 +1360,40 @@ public abstract class PermissionHolder { if (!b) { return false; } + } finally { + nodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearMetaKeys(String key, boolean temp) { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); - synchronized (nodes) { + ImmutableCollection before = getEnduringNodes().values(); + + nodesLock.lock(); + try { boolean b = this.nodes.values().removeIf(n -> n.isMeta() && (n.isTemporary() == temp) && n.getMeta().getKey().equalsIgnoreCase(key)); if (!b) { return false; } + } finally { + nodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearMetaKeys(String key, ContextSet contextSet, boolean temp) { - ImmutableSet before = ImmutableSet.copyOf(getNodes().values()); - synchronized (nodes) { + ImmutableCollection before = getEnduringNodes().values(); + nodesLock.lock(); + try { SortedSet nodes = this.nodes.get(contextSet.makeImmutable()); if (nodes == null) { return false; @@ -1215,20 +1403,28 @@ public abstract class PermissionHolder { if (!b) { return false; } + } finally { + nodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getNodes().values()); + ImmutableCollection after = getEnduringNodes().values(); plugin.getApiProvider().getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearTransientNodes() { - ImmutableSet before = ImmutableSet.copyOf(getTransientNodes().values()); - synchronized (transientNodes) { + ImmutableCollection before = getTransientNodes().values(); + + transientNodesLock.lock(); + try { transientNodes.clear(); + } finally { + transientNodesLock.unlock(); } + invalidateCache(); - ImmutableSet after = ImmutableSet.copyOf(getTransientNodes().values()); + ImmutableCollection after = getTransientNodes().values(); if (before.size() == after.size()) { return false; @@ -1242,26 +1438,26 @@ public abstract class PermissionHolder { * @return The temporary nodes held by the holder */ public Set getTemporaryNodes() { - return mergePermissionsToList().stream().filter(Node::isTemporary).collect(Collectors.toSet()); + return getOwnNodes().stream().filter(Node::isTemporary).collect(Collectors.toSet()); } /** * @return The permanent nodes held by the holder */ public Set getPermanentNodes() { - return mergePermissionsToList().stream().filter(Node::isPermanent).collect(Collectors.toSet()); + return getOwnNodes().stream().filter(Node::isPermanent).collect(Collectors.toSet()); } public Set getPrefixNodes() { - return mergePermissionsToList().stream().filter(Node::isPrefix).collect(Collectors.toSet()); + return getOwnNodes().stream().filter(Node::isPrefix).collect(Collectors.toSet()); } public Set getSuffixNodes() { - return mergePermissionsToList().stream().filter(Node::isSuffix).collect(Collectors.toSet()); + return getOwnNodes().stream().filter(Node::isSuffix).collect(Collectors.toSet()); } public Set getMetaNodes() { - return mergePermissionsToList().stream().filter(Node::isMeta).collect(Collectors.toSet()); + return getOwnNodes().stream().filter(Node::isMeta).collect(Collectors.toSet()); } public OptionalInt getWeight() { @@ -1269,7 +1465,7 @@ public abstract class PermissionHolder { OptionalInt weight = OptionalInt.empty(); try { - weight = mergePermissionsToList().stream() + weight = getOwnNodes().stream() .filter(n -> n.getPermission().startsWith("weight.")) .map(n -> n.getPermission().substring("weight.".length())) .mapToInt(Integer::parseInt) @@ -1287,7 +1483,7 @@ public abstract class PermissionHolder { } public Set getGroupReferences() { - return mergePermissionsToList().stream() + return getOwnNodes().stream() .filter(Node::isGroupNode) .map(Node::getGroupName) .map(String::toLowerCase) diff --git a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableLocalizedNode.java b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableLocalizedNode.java index 6d2ec4cb..cd36db61 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableLocalizedNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableLocalizedNode.java @@ -57,6 +57,6 @@ public final class ImmutableLocalizedNode implements LocalizedNode { @Override public boolean equals(Object obj) { - return node.equals(obj); + return this == obj || node.equals(obj); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java index b3ac255f..ff6c886c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java @@ -25,7 +25,6 @@ package me.lucko.luckperms.common.node; -import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; @@ -44,7 +43,6 @@ import me.lucko.luckperms.common.utils.PatternCache; import java.util.Collections; import java.util.Date; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -56,82 +54,8 @@ import java.util.stream.Collectors; * An immutable permission node */ @ToString(of = {"permission", "value", "override", "server", "world", "expireAt", "contexts"}) -@EqualsAndHashCode(of = {"permission", "value", "override", "server", "world", "expireAt", "contexts"}) public final class ImmutableNode implements Node { - private static boolean shouldApply(String str, boolean applyRegex, String thisStr) { - if (str.equalsIgnoreCase(thisStr)) { - return true; - } - - Set expandedStr = ShorthandParser.parseShorthand(str, false); - Set expandedThisStr = ShorthandParser.parseShorthand(thisStr, false); - - if (str.toLowerCase().startsWith("r=") && applyRegex) { - Pattern p = PatternCache.compile(str.substring(2)); - if (p == null) { - return false; - } - - for (String s : expandedThisStr) { - if (p.matcher(s).matches()) { - return true; - } - } - return false; - } - - if (thisStr.toLowerCase().startsWith("r=") && applyRegex) { - Pattern p = PatternCache.compile(thisStr.substring(2)); - if (p == null) { - return false; - } - - for (String s : expandedStr) { - if (p.matcher(s).matches()) { - return true; - } - } - return false; - } - - if (expandedStr.size() <= 1 && expandedThisStr.size() <= 1) { - return false; - } - - for (String t : expandedThisStr) { - for (String s : expandedStr) { - if (t.equalsIgnoreCase(s)) { - return true; - } - } - } - return false; - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private static boolean isInt(String a, String b) { - try { - Integer.parseInt(a); - Integer.parseInt(b); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - private static boolean isChar(String a, String b) { - return a.length() == 1 && b.length() == 1; - } - - private static Set getCharRange(char a, char b) { - Set s = new HashSet<>(); - for (char c = a; c <= b; c++) { - s.add(Character.toString(c)); - } - return s; - } - @Getter private final String permission; @@ -509,6 +433,49 @@ public final class ImmutableNode implements Node { return builder.toString(); } + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof Node)) return false; + final Node other = (Node) o; + + if (!this.permission.equals(other.getPermission())) return false; + if (!this.getValue().equals(other.getValue())) return false; + if (this.override != other.isOverride()) return false; + + final String thisServer = this.getServer().orElse(null); + final String otherServer = other.getServer().orElse(null); + if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false; + + final String thisWorld = this.getWorld().orElse(null); + final String otherWorld = other.getWorld().orElse(null); + if (thisWorld == null ? otherWorld != null : !thisWorld.equals(otherWorld)) return false; + + final long thisExpireAt = this.isTemporary() ? this.getExpiryUnixTime() : 0L; + final long otherExpireAt = other.isTemporary() ? other.getExpiryUnixTime() : 0L; + + return thisExpireAt == otherExpireAt && this.getContexts().equals(other.getContexts()); + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + + result = result * PRIME + this.permission.hashCode(); + result = result * PRIME + Boolean.hashCode(this.value); + result = result * PRIME + (this.override ? 79 : 97); + + final String server = this.getServer().orElse(null); + result = result * PRIME + (server == null ? 43 : server.hashCode()); + + final String world = this.getWorld().orElse(null); + result = result * PRIME + (world == null ? 43 : world.hashCode()); + + result = result * PRIME + (int) (this.expireAt >>> 32 ^ this.expireAt); + result = result * PRIME + this.contexts.hashCode(); + + return result; + } + @Override public boolean equalsIgnoringValue(Node other) { if (!other.getPermission().equalsIgnoreCase(this.getPermission())) { @@ -619,4 +586,55 @@ public final class ImmutableNode implements Node { public String getKey() { return getPermission(); } + + private static boolean shouldApply(String str, boolean applyRegex, String thisStr) { + if (str.equalsIgnoreCase(thisStr)) { + return true; + } + + Set expandedStr = ShorthandParser.parseShorthand(str, false); + Set expandedThisStr = ShorthandParser.parseShorthand(thisStr, false); + + if (str.toLowerCase().startsWith("r=") && applyRegex) { + Pattern p = PatternCache.compile(str.substring(2)); + if (p == null) { + return false; + } + + for (String s : expandedThisStr) { + if (p.matcher(s).matches()) { + return true; + } + } + return false; + } + + if (thisStr.toLowerCase().startsWith("r=") && applyRegex) { + Pattern p = PatternCache.compile(thisStr.substring(2)); + if (p == null) { + return false; + } + + for (String s : expandedStr) { + if (p.matcher(s).matches()) { + return true; + } + } + return false; + } + + if (expandedStr.size() <= 1 && expandedThisStr.size() <= 1) { + return false; + } + + for (String t : expandedThisStr) { + for (String s : expandedStr) { + if (t.equalsIgnoreCase(s)) { + return true; + } + } + } + return false; + } + } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeComparator.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeComparator.java index ee8681c6..ebdd8fc9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodeComparator.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeComparator.java @@ -61,7 +61,7 @@ public class NodeComparator implements Comparator { return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1; } - return NodePriorityComparator.get().compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1; + return NodeWithContextComparator.get().compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodePriorityComparator.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeWithContextComparator.java similarity index 94% rename from common/src/main/java/me/lucko/luckperms/common/node/NodePriorityComparator.java rename to common/src/main/java/me/lucko/luckperms/common/node/NodeWithContextComparator.java index c6f105f8..8beaa38b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodePriorityComparator.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeWithContextComparator.java @@ -43,10 +43,10 @@ import java.util.Locale; * Compares permission nodes based upon their supposed "priority". */ @NoArgsConstructor(access = AccessLevel.PRIVATE) -public class NodePriorityComparator implements Comparator { +public class NodeWithContextComparator implements Comparator { - private static final NodePriorityComparator INSTANCE = new NodePriorityComparator(); - public static NodePriorityComparator get() { + private static final NodeWithContextComparator INSTANCE = new NodeWithContextComparator(); + public static NodeWithContextComparator get() { return INSTANCE; } public static Comparator reverse() { diff --git a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java index 85ee01fb..21919293 100644 --- a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java @@ -55,7 +55,7 @@ public class ParentsByWeightHolder extends StoredHolder { Contexts contexts = user.getPlugin().getContextForUser(user); ContextSet contextSet = contexts != null ? contexts.getContexts() : user.getPlugin().getContextManager().getStaticContexts(); - cachedValue = user.flattenAndMergeNodesToList(contextSet).stream() + cachedValue = user.filterNodes(contextSet).stream() .filter(Node::isGroupNode) .filter(Node::getValue) .map(n -> Optional.ofNullable(user.getPlugin().getGroupManager().getIfLoaded(n.getGroupName()))) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/file/JSONBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/file/JSONBacking.java index 78aec200..d31f3051 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/file/JSONBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/file/JSONBacking.java @@ -170,7 +170,7 @@ public class JSONBacking extends FlatfileBacking { Set data = deserializePermissions(object.get("permissions").getAsJsonArray()); Set nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet()); - user.setNodes(nodes); + user.setEnduringNodes(nodes); user.setName(name, true); boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); @@ -222,7 +222,7 @@ public class JSONBacking extends FlatfileBacking { data.addProperty("name", user.getName().orElse("null")); data.addProperty("primaryGroup", user.getPrimaryGroup().getStoredValue()); - Set nodes = user.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); + Set nodes = user.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); data.add("permissions", serializePermissions(nodes)); return writeElementToFile(userFile, data); @@ -311,7 +311,7 @@ public class JSONBacking extends FlatfileBacking { JsonObject object = readObjectFromFile(groupFile); Set data = deserializePermissions(object.get("permissions").getAsJsonArray()); Set nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet()); - group.setNodes(nodes); + group.setEnduringNodes(nodes); return true; } else { groupFile.createNewFile(); @@ -319,7 +319,7 @@ public class JSONBacking extends FlatfileBacking { JsonObject data = new JsonObject(); data.addProperty("name", group.getName()); - Set nodes = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); + Set nodes = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); data.add("permissions", serializePermissions(nodes)); return writeElementToFile(groupFile, data); @@ -346,7 +346,7 @@ public class JSONBacking extends FlatfileBacking { JsonObject object = readObjectFromFile(groupFile); Set data = deserializePermissions(object.get("permissions").getAsJsonArray()); Set nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet()); - group.setNodes(nodes); + group.setEnduringNodes(nodes); return true; }, false); } finally { @@ -368,7 +368,7 @@ public class JSONBacking extends FlatfileBacking { JsonObject data = new JsonObject(); data.addProperty("name", group.getName()); - Set nodes = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); + Set nodes = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); data.add("permissions", serializePermissions(nodes)); return writeElementToFile(groupFile, data); }, false); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/file/YAMLBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/file/YAMLBacking.java index 0c1cfda9..24ab63c8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/file/YAMLBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/file/YAMLBacking.java @@ -174,7 +174,7 @@ public class YAMLBacking extends FlatfileBacking { Set data = deserializePermissions((List) values.get("permissions")); Set nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet()); - user.setNodes(nodes); + user.setEnduringNodes(nodes); user.setName(name, true); boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); @@ -224,7 +224,7 @@ public class YAMLBacking extends FlatfileBacking { values.put("name", user.getName().orElse("null")); values.put("primary-group", user.getPrimaryGroup().getStoredValue()); - Set data = user.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); + Set data = user.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); values.put("permissions", serializePermissions(data)); return writeMapToFile(userFile, values); @@ -313,14 +313,14 @@ public class YAMLBacking extends FlatfileBacking { Map values = readMapFromFile(groupFile); Set data = deserializePermissions((List) values.get("permissions")); Set nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet()); - group.setNodes(nodes); + group.setEnduringNodes(nodes); return true; } else { groupFile.createNewFile(); Map values = new LinkedHashMap<>(); values.put("name", group.getName()); - Set data = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); + Set data = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); values.put("permissions", serializePermissions(data)); return writeMapToFile(groupFile, values); } @@ -346,7 +346,7 @@ public class YAMLBacking extends FlatfileBacking { Map values = readMapFromFile(groupFile); Set data = deserializePermissions((List) values.get("permissions")); Set nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet()); - group.setNodes(nodes); + group.setEnduringNodes(nodes); return true; }, false); } finally { @@ -368,7 +368,7 @@ public class YAMLBacking extends FlatfileBacking { Map values = new LinkedHashMap<>(); values.put("name", group.getName()); - Set data = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); + Set data = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new)); values.put("permissions", serializePermissions(data)); return writeMapToFile(groupFile, values); }, false); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java index 9b849bb3..da48dcef 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java @@ -106,7 +106,7 @@ public class MongoDBBacking extends AbstractBacking { .append("primaryGroup", user.getPrimaryGroup().getStoredValue()); Document perms = new Document(); - for (Map.Entry e : convert(exportToLegacy(user.getNodes().values())).entrySet()) { + for (Map.Entry e : convert(exportToLegacy(user.getEnduringNodes().values())).entrySet()) { perms.append(e.getKey(), e.getValue()); } @@ -118,7 +118,7 @@ public class MongoDBBacking extends AbstractBacking { Document main = new Document("_id", group.getName()); Document perms = new Document(); - for (Map.Entry e : convert(exportToLegacy(group.getNodes().values())).entrySet()) { + for (Map.Entry e : convert(exportToLegacy(group.getEnduringNodes().values())).entrySet()) { perms.append(e.getKey(), e.getValue()); } @@ -348,7 +348,7 @@ public class MongoDBBacking extends AbstractBacking { if (cursor.hasNext()) { // User exists, let's load. Document d = cursor.next(); - user.setNodes(revert((Map) d.get("perms")).entrySet().stream() + user.setEnduringNodes(revert((Map) d.get("perms")).entrySet().stream() .map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue())) .collect(Collectors.toSet()) ); @@ -476,7 +476,7 @@ public class MongoDBBacking extends AbstractBacking { if (cursor.hasNext()) { // Group exists, let's load. Document d = cursor.next(); - group.setNodes(revert((Map) d.get("perms")).entrySet().stream() + group.setEnduringNodes(revert((Map) d.get("perms")).entrySet().stream() .map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue())) .collect(Collectors.toSet()) ); @@ -503,7 +503,7 @@ public class MongoDBBacking extends AbstractBacking { if (cursor.hasNext()) { Document d = cursor.next(); - group.setNodes(revert((Map) d.get("perms")).entrySet().stream() + group.setEnduringNodes(revert((Map) d.get("perms")).entrySet().stream() .map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue())) .collect(Collectors.toSet()) ); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/sql/SQLBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/sql/SQLBacking.java index 6281f53f..ec9fcc6d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/sql/SQLBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/sql/SQLBacking.java @@ -359,7 +359,7 @@ public class SQLBacking extends AbstractBacking { // If the user has any data in storage if (!data.isEmpty()) { Set nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet()); - user.setNodes(nodes); + user.setEnduringNodes(nodes); // Save back to the store if data was changed if (plugin.getUserManager().giveDefaultIfNeeded(user, false)) { @@ -428,7 +428,7 @@ public class SQLBacking extends AbstractBacking { return false; } - Set local = user.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toSet()); + Set local = user.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toSet()); Map.Entry, Set> diff = compareSets(local, remote); @@ -626,7 +626,7 @@ public class SQLBacking extends AbstractBacking { if (!data.isEmpty()) { Set nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet()); - group.setNodes(nodes); + group.setEnduringNodes(nodes); } else { group.clearNodes(); } @@ -675,7 +675,7 @@ public class SQLBacking extends AbstractBacking { group.getIoLock().lock(); try { // Empty data, just delete. - if (group.getNodes().isEmpty()) { + if (group.getEnduringNodes().isEmpty()) { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(prefix.apply(GROUP_PERMISSIONS_DELETE))) { ps.setString(1, group.getName()); @@ -711,7 +711,7 @@ public class SQLBacking extends AbstractBacking { return false; } - Set local = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toSet()); + Set local = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toSet()); Map.Entry, Set> diff = compareSets(local, remote); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionManager.java index 23c237bd..155245a7 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionManager.java @@ -130,7 +130,7 @@ public class MigrationPermissionManager extends SubCommand { // Make a LuckPerms user for the one being migrated plugin.getStorage().loadUser(uuid, "null").join(); User user = plugin.getUserManager().getIfLoaded(uuid); - if (user.getNodes().size() <= 1) { + if (user.getEnduringNodes().size() <= 1) { user.clearNodes(false); } migrateSubject(pmUser, user, 100); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionsEx.java b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionsEx.java index dcd4af70..708371c3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionsEx.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionsEx.java @@ -186,7 +186,7 @@ public class MigrationPermissionsEx extends SubCommand { // Make a LuckPerms user for the one being migrated plugin.getStorage().loadUser(uuid, "null").join(); User user = plugin.getUserManager().getIfLoaded(uuid); - if (user.getNodes().size() <= 1) { + if (user.getEnduringNodes().size() <= 1) { user.clearNodes(false); } migrateSubject(pexUser, user, maxWeight); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java index bf03ac97..070679d5 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java @@ -78,7 +78,7 @@ public class LuckPermsSubjectData implements LPSubjectData { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) { Map> perms = new HashMap<>(); - for (Map.Entry> e : (enduring ? holder.getNodes() : holder.getTransientNodes()).asMap().entrySet()) { + for (Map.Entry> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) { ImmutableMap.Builder results = ImmutableMap.builder(); for (Node n : e.getValue()) { results.put(n.getPermission(), n.getValue()); @@ -184,7 +184,7 @@ public class LuckPermsSubjectData implements LPSubjectData { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PARENTS)) { Map> parents = new HashMap<>(); - for (Map.Entry> e : (enduring ? holder.getNodes() : holder.getTransientNodes()).asMap().entrySet()) { + for (Map.Entry> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) { ImmutableList.Builder results = ImmutableList.builder(); for (Node n : e.getValue()) { if (n.isGroupNode()) { @@ -321,7 +321,7 @@ public class LuckPermsSubjectData implements LPSubjectData { Map minPrefixPriority = new HashMap<>(); Map minSuffixPriority = new HashMap<>(); - for (Node n : enduring ? holder.getNodes().values() : holder.getTransientNodes().values()) { + for (Node n : enduring ? holder.getEnduringNodes().values() : holder.getTransientNodes().values()) { if (!n.getValue()) continue; if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue; @@ -460,7 +460,7 @@ public class LuckPermsSubjectData implements LPSubjectData { } private Stream streamNodes(boolean enduring) { - return (enduring ? holder.getNodes() : holder.getTransientNodes()).values().stream(); + return (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).values().stream(); } private Consumer makeUnsetConsumer(boolean enduring) { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java index 3eaab3ae..76f56bc5 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java @@ -37,7 +37,7 @@ import com.google.gson.JsonObject; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.contexts.ContextSetComparator; import me.lucko.luckperms.common.node.NodeModel; -import me.lucko.luckperms.common.node.NodePriorityComparator; +import me.lucko.luckperms.common.node.NodeWithContextComparator; import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.SubjectReference; @@ -194,7 +194,7 @@ public class SubjectStorageModel { // sort alphabetically. List> perms = new ArrayList<>(e.getValue().entrySet()); - perms.sort((o1, o2) -> NodePriorityComparator.get().compareStrings(o1.getKey(), o2.getKey())); + perms.sort((o1, o2) -> NodeWithContextComparator.get().compareStrings(o1.getKey(), o2.getKey())); for (Map.Entry ent : perms) { data.addProperty(ent.getKey(), ent.getValue()); @@ -218,7 +218,7 @@ public class SubjectStorageModel { // sort alphabetically. List> opts = new ArrayList<>(e.getValue().entrySet()); - opts.sort((o1, o2) -> NodePriorityComparator.get().compareStrings(o1.getKey(), o2.getKey())); + opts.sort((o1, o2) -> NodeWithContextComparator.get().compareStrings(o1.getKey(), o2.getKey())); for (Map.Entry ent : opts) { data.addProperty(ent.getKey(), ent.getValue());