Optimizations to the core PermissionHolder class

- Remove usage of Java 8 streams from frequently called methods
- Use LinkedLists where appropriate to improve Iterator#remove speed
- Cache immutable multimap conversion
- Rename some methods to make the intended use/behaviour clearer
This commit is contained in:
Luck 2017-08-13 19:24:00 +02:00
parent d98b464ce9
commit a40421fa1f
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
40 changed files with 690 additions and 467 deletions

View File

@ -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;

View File

@ -225,7 +225,7 @@ public class VaultPermissionHook extends Permission {
if (group == null) return false;
// This is a nasty call. Groups aren't cached. :(
Map<String, Boolean> permissions = group.exportNodes(ExtractedContexts.generate(createContextForWorldLookup(world)), true);
Map<String, Boolean> 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<String> localGroups = user.mergePermissionsToList().stream()
List<String> localGroups = user.getOwnNodes().stream()
.filter(Node::isGroupNode)
.filter(n -> n.shouldApplyOnWorld(finalWorld, isIncludeGlobal(), true))
.filter(n -> n.shouldApplyOnServer(getServer(), isIncludeGlobal(), true))

View File

@ -153,7 +153,7 @@ public final class GroupDelegate extends PermissionHolderDelegate implements Gro
@Override
public List<String> 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<String> 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<String> 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)

View File

@ -74,12 +74,12 @@ public class PermissionHolderDelegate implements PermissionHolder {
@Override
public SortedSet<? extends Node> getPermissions() {
return ImmutableSortedSet.copyOfSorted(handle.mergePermissionsToSortedSet());
return ImmutableSortedSet.copyOfSorted(handle.getOwnNodesSorted());
}
@Override
public Set<Node> 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<String, Boolean> exportNodes(Contexts contexts, boolean lowerCase) {
return new HashMap<>(handle.exportNodes(ExtractedContexts.generate(contexts), lowerCase));
return new HashMap<>(handle.exportNodesAndShorthand(ExtractedContexts.generate(contexts), lowerCase));
}
@Override

View File

@ -198,7 +198,7 @@ public final class UserDelegate extends PermissionHolderDelegate implements User
@Override
public List<String> 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<String> 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<String> 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)

View File

@ -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 <T> the type being stored
*/
@RequiredArgsConstructor
public class Cache<T> {
public abstract class Cache<T> {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Supplier<T> 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<T> getIfPresent() {
public final Optional<T> getIfPresent() {
lock.readLock().lock();
try {
return Optional.ofNullable(cached);
@ -76,7 +81,7 @@ public class Cache<T> {
}
}
public void invalidate() {
public final void invalidate() {
lock.writeLock().lock();
try {
cached = null;

View File

@ -61,33 +61,11 @@ public class UserCache implements UserData {
private final LoadingCache<Contexts, PermissionCache> permission = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(new CacheLoader<Contexts, PermissionCache>() {
@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<MetaContexts, MetaCache> meta = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(new CacheLoader<MetaContexts, MetaCache>() {
@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<Contexts, PermissionCache> {
@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<MetaContexts, MetaCache> {
@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,

View File

@ -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 {

View File

@ -59,7 +59,7 @@ public class HolderClear<T extends PermissionHolder> extends SubCommand<T> {
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<T extends PermissionHolder> extends SubCommand<T> {
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 {

View File

@ -83,7 +83,7 @@ public class HolderEditor<T extends PermissionHolder> extends SubCommand<T> {
}
JsonObject data = new JsonObject();
Set<NodeModel> nodes = holder.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
Set<NodeModel> nodes = holder.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
data.add("nodes", serializePermissions(nodes));
data.addProperty("who", id(holder));

View File

@ -61,7 +61,7 @@ public class HolderShowTracks<T extends PermissionHolder> extends SubCommand<T>
return CommandResult.LOADING_ERROR;
}
Set<Node> nodes = holder.getNodes().values().stream()
Set<Node> nodes = holder.getEnduringNodes().values().stream()
.filter(Node::isGroupNode)
.filter(Node::isPermanent)
.collect(Collectors.toSet());

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -88,7 +88,7 @@ public class PermissionInfo extends SharedSubCommand {
int page = ArgumentUtils.handleIntOrElse(0, args, 1);
Map.Entry<Component, String> ent = nodesToMessage(false, filter, holder.mergePermissionsToSortedSet(), holder, label, page, sender.isConsole());
Map.Entry<Component, String> 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<Component, String> tempEnt = nodesToMessage(true, filter, holder.mergePermissionsToSortedSet(), holder, label, page, sender.isConsole());
Map.Entry<Component, String> 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());

View File

@ -74,7 +74,7 @@ public class GroupClone extends SubCommand<Group> {
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);

View File

@ -68,12 +68,12 @@ public class GroupInfo extends SubCommand<Group> {
group.getMetaNodes().size()
);
Set<Node> parents = group.mergePermissions().stream()
Set<Node> parents = group.getOwnNodesSet().stream()
.filter(Node::isGroupNode)
.filter(Node::isPermanent)
.collect(Collectors.toSet());
Set<Node> tempParents = group.mergePermissions().stream()
Set<Node> tempParents = group.getOwnNodesSet().stream()
.filter(Node::isGroupNode)
.filter(Node::isTemporary)
.collect(Collectors.toSet());

View File

@ -77,7 +77,7 @@ public class GroupRename extends SubCommand<Group> {
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);

View File

@ -141,7 +141,7 @@ public class ApplyEditsCommand extends SingleCommand {
}
Set<NodeModel> 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;

View File

@ -97,7 +97,7 @@ public class UserDemote extends SubCommand<User> {
}
// Load applicable groups
Set<Node> nodes = user.getNodes().values().stream()
Set<Node> nodes = user.getEnduringNodes().values().stream()
.filter(Node::isGroupNode)
.filter(Node::getValue)
.filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable()))

View File

@ -72,12 +72,12 @@ public class UserInfo extends SubCommand<User> {
user.getMetaNodes().size()
);
Set<Node> parents = user.mergePermissions().stream()
Set<Node> parents = user.getOwnNodesSet().stream()
.filter(Node::isGroupNode)
.filter(Node::isPermanent)
.collect(Collectors.toSet());
Set<Node> tempParents = user.mergePermissions().stream()
Set<Node> tempParents = user.getOwnNodesSet().stream()
.filter(Node::isGroupNode)
.filter(Node::isTemporary)
.collect(Collectors.toSet());

View File

@ -95,7 +95,7 @@ public class UserPromote extends SubCommand<User> {
}
// Load applicable groups
Set<Node> nodes = user.getNodes().values().stream()
Set<Node> nodes = user.getEnduringNodes().values().stream()
.filter(Node::isGroupNode)
.filter(Node::getValue)
.filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable()))

View File

@ -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;

View File

@ -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<Node> before, Set<Node> after) {
public void handleNodeAdd(Node node, PermissionHolder target, Collection<Node> before, Collection<Node> after) {
EventNodeAdd event = new EventNodeAdd(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEvent(event);
}
public void handleNodeClear(PermissionHolder target, Set<Node> before, Set<Node> after) {
public void handleNodeClear(PermissionHolder target, Collection<Node> before, Collection<Node> after) {
EventNodeClear event = new EventNodeClear(target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEvent(event);
}
public void handleNodeRemove(Node node, PermissionHolder target, Set<Node> before, Set<Node> after) {
public void handleNodeRemove(Node node, PermissionHolder target, Collection<Node> before, Collection<Node> after) {
EventNodeRemove event = new EventNodeRemove(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEvent(event);
}

View File

@ -43,7 +43,7 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> 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<UserIdentifier, User> 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;

View File

@ -50,12 +50,12 @@ public class Group extends PermissionHolder implements Identifiable<String> {
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() {

View File

@ -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);
}
}

View File

@ -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<String> expandedStr = ShorthandParser.parseShorthand(str, false);
Set<String> 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<String> getCharRange(char a, char b) {
Set<String> 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<String> expandedStr = ShorthandParser.parseShorthand(str, false);
Set<String> 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;
}
}

View File

@ -61,7 +61,7 @@ public class NodeComparator implements Comparator<Node> {
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;
}
}

View File

@ -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<LocalizedNode> {
public class NodeWithContextComparator implements Comparator<LocalizedNode> {
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<LocalizedNode> reverse() {

View File

@ -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())))

View File

@ -170,7 +170,7 @@ public class JSONBacking extends FlatfileBacking {
Set<NodeModel> data = deserializePermissions(object.get("permissions").getAsJsonArray());
Set<Node> 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<NodeModel> nodes = user.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
Set<NodeModel> 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<NodeModel> data = deserializePermissions(object.get("permissions").getAsJsonArray());
Set<Node> 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<NodeModel> nodes = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
Set<NodeModel> 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<NodeModel> data = deserializePermissions(object.get("permissions").getAsJsonArray());
Set<Node> 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<NodeModel> nodes = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
Set<NodeModel> nodes = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
data.add("permissions", serializePermissions(nodes));
return writeElementToFile(groupFile, data);
}, false);

View File

@ -174,7 +174,7 @@ public class YAMLBacking extends FlatfileBacking {
Set<NodeModel> data = deserializePermissions((List<Object>) values.get("permissions"));
Set<Node> 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<NodeModel> data = user.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
Set<NodeModel> 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<String, Object> values = readMapFromFile(groupFile);
Set<NodeModel> data = deserializePermissions((List<Object>) values.get("permissions"));
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
group.setNodes(nodes);
group.setEnduringNodes(nodes);
return true;
} else {
groupFile.createNewFile();
Map<String, Object> values = new LinkedHashMap<>();
values.put("name", group.getName());
Set<NodeModel> data = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
Set<NodeModel> 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<String, Object> values = readMapFromFile(groupFile);
Set<NodeModel> data = deserializePermissions((List<Object>) values.get("permissions"));
Set<Node> 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<String, Object> values = new LinkedHashMap<>();
values.put("name", group.getName());
Set<NodeModel> data = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
Set<NodeModel> data = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
values.put("permissions", serializePermissions(data));
return writeMapToFile(groupFile, values);
}, false);

View File

@ -106,7 +106,7 @@ public class MongoDBBacking extends AbstractBacking {
.append("primaryGroup", user.getPrimaryGroup().getStoredValue());
Document perms = new Document();
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(user.getNodes().values())).entrySet()) {
for (Map.Entry<String, Boolean> 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<String, Boolean> e : convert(exportToLegacy(group.getNodes().values())).entrySet()) {
for (Map.Entry<String, Boolean> 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<String, Boolean>) d.get("perms")).entrySet().stream()
user.setEnduringNodes(revert((Map<String, Boolean>) 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<String, Boolean>) d.get("perms")).entrySet().stream()
group.setEnduringNodes(revert((Map<String, Boolean>) 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<String, Boolean>) d.get("perms")).entrySet().stream()
group.setEnduringNodes(revert((Map<String, Boolean>) d.get("perms")).entrySet().stream()
.map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue()))
.collect(Collectors.toSet())
);

View File

@ -359,7 +359,7 @@ public class SQLBacking extends AbstractBacking {
// If the user has any data in storage
if (!data.isEmpty()) {
Set<Node> 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<NodeModel> local = user.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toSet());
Set<NodeModel> local = user.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toSet());
Map.Entry<Set<NodeModel>, Set<NodeModel>> diff = compareSets(local, remote);
@ -626,7 +626,7 @@ public class SQLBacking extends AbstractBacking {
if (!data.isEmpty()) {
Set<Node> 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<NodeModel> local = group.getNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toSet());
Set<NodeModel> local = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toSet());
Map.Entry<Set<NodeModel>, Set<NodeModel>> diff = compareSets(local, remote);

View File

@ -130,7 +130,7 @@ public class MigrationPermissionManager extends SubCommand<Object> {
// 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);

View File

@ -186,7 +186,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
// 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);

View File

@ -78,7 +78,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) {
Map<ImmutableContextSet, ImmutableMap.Builder<String, Boolean>> perms = new HashMap<>();
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getNodes() : holder.getTransientNodes()).asMap().entrySet()) {
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) {
ImmutableMap.Builder<String, Boolean> 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<ImmutableContextSet, ImmutableList.Builder<SubjectReference>> parents = new HashMap<>();
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getNodes() : holder.getTransientNodes()).asMap().entrySet()) {
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) {
ImmutableList.Builder<SubjectReference> results = ImmutableList.builder();
for (Node n : e.getValue()) {
if (n.isGroupNode()) {
@ -321,7 +321,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
Map<ImmutableContextSet, Integer> minPrefixPriority = new HashMap<>();
Map<ImmutableContextSet, Integer> 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<Node> streamNodes(boolean enduring) {
return (enduring ? holder.getNodes() : holder.getTransientNodes()).values().stream();
return (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).values().stream();
}
private Consumer<Node> makeUnsetConsumer(boolean enduring) {

View File

@ -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<Map.Entry<String, Boolean>> 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<String, Boolean> ent : perms) {
data.addProperty(ent.getKey(), ent.getValue());
@ -218,7 +218,7 @@ public class SubjectStorageModel {
// sort alphabetically.
List<Map.Entry<String, String>> 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<String, String> ent : opts) {
data.addProperty(ent.getKey(), ent.getValue());