diff --git a/api/src/main/java/me/lucko/luckperms/api/Node.java b/api/src/main/java/me/lucko/luckperms/api/Node.java index 67d27210..ad277631 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Node.java +++ b/api/src/main/java/me/lucko/luckperms/api/Node.java @@ -30,6 +30,7 @@ import com.google.common.base.Preconditions; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.nodetype.NodeType; import me.lucko.luckperms.api.nodetype.NodeTypeKey; +import me.lucko.luckperms.api.nodetype.types.DisplayNameType; import me.lucko.luckperms.api.nodetype.types.InheritanceType; import me.lucko.luckperms.api.nodetype.types.MetaType; import me.lucko.luckperms.api.nodetype.types.PrefixType; @@ -95,6 +96,7 @@ import javax.annotation.concurrent.Immutable; *
  • {@link SuffixType} - represents an assigned suffix
  • *
  • {@link MetaType} - represents an assigned meta option
  • *
  • {@link WeightType} - marks the weight of the object holding this node
  • + *
  • {@link DisplayNameType} - marks the display name of the object holding this node
  • * * *

    The core node state must be immutable in all implementations.

    diff --git a/api/src/main/java/me/lucko/luckperms/api/nodetype/types/DisplayNameType.java b/api/src/main/java/me/lucko/luckperms/api/nodetype/types/DisplayNameType.java new file mode 100644 index 00000000..34547034 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/nodetype/types/DisplayNameType.java @@ -0,0 +1,54 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.api.nodetype.types; + +import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.nodetype.NodeType; +import me.lucko.luckperms.api.nodetype.NodeTypeKey; + +import javax.annotation.Nonnull; + +/** + * A sub-type of {@link Node} used to mark the display name of the node's holder. + * + * @since 4.2 + */ +public interface DisplayNameType extends NodeType { + + /** + * The key for this type. + */ + NodeTypeKey KEY = new NodeTypeKey(){}; + + /** + * Gets the display name. + * + * @return the display name + */ + @Nonnull + String getDisplayName(); + +} 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 6ced4455..0ed2464f 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 @@ -26,63 +26,42 @@ package me.lucko.luckperms.common.buffers; import java.util.Optional; -import java.util.concurrent.locks.ReentrantReadWriteLock; + +import javax.annotation.Nonnull; /** - * Thread-safe caching utility + * Simple one element cache implementation. * * @param the type being stored */ public abstract class Cache { - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - private T cached = null; + private volatile T value = null; + @Nonnull protected abstract T supply(); public final T get() { - // try to just read from the cached value - this.lock.readLock().lock(); - try { - if (this.cached != null) { - return this.cached; + T val = this.value; + + // double checked locking + if (val == null) { + synchronized (this) { + val = this.value; + if (val == null) { + val = supply(); + this.value = val; + } } - } finally { - // we have to release the read lock, as it is not possible - // to acquire the write lock whilst holding a read lock - this.lock.readLock().unlock(); } - this.lock.writeLock().lock(); - try { - // Since the lock was unlocked momentarily, we need - // to check again for a cached value - if (this.cached != null) { - return this.cached; - } - - // call the supplier and set the cached value - this.cached = supply(); - return this.cached; - } finally { - this.lock.writeLock().unlock(); - } + return val; } public final Optional getIfPresent() { - this.lock.readLock().lock(); - try { - return Optional.ofNullable(this.cached); - } finally { - this.lock.readLock().unlock(); - } + return Optional.ofNullable(this.value); } public final void invalidate() { - this.lock.writeLock().lock(); - try { - this.cached = null; - } finally { - this.lock.writeLock().unlock(); - } + this.value = null; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupSetDisplayName.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupSetDisplayName.java index 04f42fd1..9035c36a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupSetDisplayName.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupSetDisplayName.java @@ -25,12 +25,16 @@ package me.lucko.luckperms.common.commands.group; +import me.lucko.luckperms.api.context.MutableContextSet; +import me.lucko.luckperms.api.nodetype.types.DisplayNameType; import me.lucko.luckperms.common.actionlog.ExtendedLogEntry; import me.lucko.luckperms.common.command.CommandResult; +import me.lucko.luckperms.common.command.abstraction.CommandException; import me.lucko.luckperms.common.command.abstraction.SubCommand; import me.lucko.luckperms.common.command.access.ArgumentPermissions; import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.command.utils.ArgumentParser; +import me.lucko.luckperms.common.command.utils.MessageUtils; import me.lucko.luckperms.common.command.utils.StorageAssistant; import me.lucko.luckperms.common.locale.LocaleManager; import me.lucko.luckperms.common.locale.command.CommandSpec; @@ -45,18 +49,20 @@ import java.util.List; public class GroupSetDisplayName extends SubCommand { public GroupSetDisplayName(LocaleManager locale) { - super(CommandSpec.GROUP_SET_DISPLAY_NAME.localize(locale), "setdisplayname", CommandPermission.GROUP_SET_DISPLAY_NAME, Predicates.not(1)); + super(CommandSpec.GROUP_SET_DISPLAY_NAME.localize(locale), "setdisplayname", CommandPermission.GROUP_SET_DISPLAY_NAME, Predicates.is(0)); } @Override - public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List args, String label) { + public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List args, String label) throws CommandException { if (ArgumentPermissions.checkModifyPerms(plugin, sender, getPermission().get(), group)) { Message.COMMAND_NO_PERMISSION.send(sender); return CommandResult.NO_PERMISSION; } String name = ArgumentParser.parseString(0, args); - String previousName = group.getDisplayName().orElse(null); + MutableContextSet context = ArgumentParser.parseContext(1, args, plugin); + + String previousName = group.getDisplayName(context).orElse(null); if (previousName == null && name.equals(group.getName())) { Message.GROUP_SET_DISPLAY_NAME_DOESNT_HAVE.send(sender, group.getName()); @@ -74,25 +80,25 @@ public class GroupSetDisplayName extends SubCommand { return CommandResult.STATE_ERROR; } - group.removeIf(n -> n.getPermission().startsWith("displayname.")); + group.removeIf(context, n -> n.getTypeData(DisplayNameType.KEY).isPresent()); if (name.equals(group.getName())) { - Message.GROUP_SET_DISPLAY_NAME_REMOVED.send(sender, group.getName()); + Message.GROUP_SET_DISPLAY_NAME_REMOVED.send(sender, group.getName(), MessageUtils.contextSetToString(context)); ExtendedLogEntry.build().actor(sender).acted(group) - .action("setdisplayname", name) + .action("setdisplayname", name, context) .build().submit(plugin, sender); StorageAssistant.save(group, sender, plugin); return CommandResult.SUCCESS; } - group.setPermission(NodeFactory.builder("displayname." + name).build()); + group.setPermission(NodeFactory.builder("displayname." + name).withExtraContext(context).build()); - Message.GROUP_SET_DISPLAY_NAME.send(sender, name, group.getName()); + Message.GROUP_SET_DISPLAY_NAME.send(sender, name, group.getName(), MessageUtils.contextSetToString(context)); ExtendedLogEntry.build().actor(sender).acted(group) - .action("setdisplayname", name) + .action("setdisplayname", name, context) .build().submit(plugin, sender); StorageAssistant.save(group, sender, plugin); diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/command/CommandSpec.java b/common/src/main/java/me/lucko/luckperms/common/locale/command/CommandSpec.java index 6e592381..fde271f5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/command/CommandSpec.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/command/CommandSpec.java @@ -169,7 +169,8 @@ public enum CommandSpec { ), GROUP_SET_DISPLAY_NAME("Set the groups display name", Argument.list( - Argument.create("name", true, "the name to set") + Argument.create("name", true, "the name to set"), + Argument.create("context...", false, "the contexts to set the name in") ) ), GROUP_RENAME("Rename the group", diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/message/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/message/Message.java index b3cfb1df..f5bc2a4c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/message/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/message/Message.java @@ -364,8 +364,8 @@ public enum Message { GROUP_SET_DISPLAY_NAME_DOESNT_HAVE("&b{}&a doesn't have a display name set.", true), GROUP_SET_DISPLAY_NAME_ALREADY_HAS("&b{}&a already has a display name of &b{}&a.", true), GROUP_SET_DISPLAY_NAME_ALREADY_IN_USE("&aThe display name &b{}&a is already being used by &b{}&a.", true), - GROUP_SET_DISPLAY_NAME("&aSet display name to &b{}&a for group &b{}&a.", true), - GROUP_SET_DISPLAY_NAME_REMOVED("&aRemoved display name for group &b{}&a.", true), + GROUP_SET_DISPLAY_NAME("&aSet display name to &b{}&a for group &b{}&a in context {}&a.", true), + GROUP_SET_DISPLAY_NAME_REMOVED("&aRemoved display name for group &b{}&a in context {}&a.", true), TRACK_INFO( "{PREFIX}&b&l> &bShowing Track: &f{}" + "\n" + diff --git a/common/src/main/java/me/lucko/luckperms/common/managers/group/AbstractGroupManager.java b/common/src/main/java/me/lucko/luckperms/common/managers/group/AbstractGroupManager.java index fc53b7bf..0ae70a6f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/managers/group/AbstractGroupManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/managers/group/AbstractGroupManager.java @@ -28,6 +28,8 @@ package me.lucko.luckperms.common.managers.group; import me.lucko.luckperms.common.managers.AbstractManager; import me.lucko.luckperms.common.model.Group; +import java.util.Optional; + public abstract class AbstractGroupManager extends AbstractManager implements GroupManager { @Override @@ -40,14 +42,16 @@ public abstract class AbstractGroupManager extends AbstractMana // then try exact display name matches for (T group : getAll().values()) { - if (group.getDisplayName().isPresent() && group.getDisplayName().get().equals(name)) { + Optional displayName = group.getDisplayName(); + if (displayName.isPresent() && displayName.get().equals(name)) { return group; } } // then try case insensitive name matches for (T group : getAll().values()) { - if (group.getDisplayName().isPresent() && group.getDisplayName().get().equalsIgnoreCase(name)) { + Optional displayName = group.getDisplayName(); + if (displayName.isPresent() && displayName.get().equalsIgnoreCase(name)) { return group; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/DisplayNameCache.java b/common/src/main/java/me/lucko/luckperms/common/model/DisplayNameCache.java new file mode 100644 index 00000000..94dc65e7 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/model/DisplayNameCache.java @@ -0,0 +1,62 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.model; + +import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.nodetype.types.DisplayNameType; +import me.lucko.luckperms.common.buffers.Cache; +import me.lucko.luckperms.common.config.ConfigKeys; + +import java.util.Optional; + +import javax.annotation.Nonnull; + +/** + * Cache instance to supply the display name of a {@link Group}. + */ +public class DisplayNameCache extends Cache> { + private final Group group; + + public DisplayNameCache(Group group) { + this.group = group; + } + + @Nonnull + @Override + protected Optional supply() { + // query for a displayname node + for (Node n : this.group.getOwnNodes(this.group.getPlugin().getContextManager().getStaticContext())) { + Optional displayName = n.getTypeData(DisplayNameType.KEY); + if (displayName.isPresent()) { + return Optional.of(displayName.get().getDisplayName()); + } + } + + // fallback to config + String name = this.group.getPlugin().getConfiguration().get(ConfigKeys.GROUP_NAME_REWRITES).get(this.group.getObjectName()); + return name == null || name.equals(this.group.getObjectName()) ? Optional.empty() : Optional.of(name); + } +} 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 2718d18c..4eed7d47 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 @@ -26,12 +26,12 @@ package me.lucko.luckperms.common.model; import me.lucko.luckperms.api.Node; -import me.lucko.luckperms.api.context.ImmutableContextSet; +import me.lucko.luckperms.api.context.ContextSet; +import me.lucko.luckperms.api.nodetype.types.DisplayNameType; import me.lucko.luckperms.common.api.delegates.model.ApiGroup; import me.lucko.luckperms.common.buffers.BufferedRequest; import me.lucko.luckperms.common.buffers.Cache; import me.lucko.luckperms.common.caching.GroupCachedData; -import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import java.util.Optional; @@ -47,11 +47,15 @@ public class Group extends PermissionHolder implements Identifiable { private final String name; /** - * Caches the holders weight - * @see #getWeight() + * Caches the groups weight */ private final Cache weightCache = new WeightCache(this); + /** + * Caches the groups display name + */ + private final Cache> displayNameCache = new DisplayNameCache(this); + /** * The groups data cache instance */ @@ -77,6 +81,7 @@ public class Group extends PermissionHolder implements Identifiable { @Override protected void invalidateCache() { this.weightCache.invalidate(); + this.displayNameCache.invalidate(); super.invalidateCache(); } @@ -104,22 +109,17 @@ public class Group extends PermissionHolder implements Identifiable { } public Optional getDisplayName() { - String name = null; - for (Node n : enduringData().immutable().get(ImmutableContextSet.empty())) { - if (!n.getPermission().startsWith("displayname.")) { - continue; + return this.displayNameCache.get(); + } + + public Optional getDisplayName(ContextSet contextSet) { + for (Node n : getData(NodeMapType.ENDURING).immutable().get(contextSet.makeImmutable())) { + Optional displayName = n.getTypeData(DisplayNameType.KEY); + if (displayName.isPresent()) { + return Optional.of(displayName.get().getDisplayName()); } - - name = n.getPermission().substring("displayname.".length()); - break; } - - if (name != null) { - return Optional.of(name); - } - - name = getPlugin().getConfiguration().get(ConfigKeys.GROUP_NAME_REWRITES).get(getObjectName()); - return name == null || name.equals(getObjectName()) ? Optional.empty() : Optional.of(name); + return Optional.empty(); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java b/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java index 7a23d0d6..6bdd19b1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java @@ -53,6 +53,7 @@ import java.util.TreeSet; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Predicate; +import javax.annotation.Nonnull; import javax.annotation.Nullable; /** @@ -363,6 +364,7 @@ public final class NodeMap { this.handle = handle; } + @Nonnull @Override protected ImmutableSetMultimap supply() { this.handle.lock.lock(); diff --git a/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java b/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java index a77899ce..3923de9f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java @@ -35,8 +35,10 @@ import java.util.Map; import java.util.Optional; import java.util.OptionalInt; +import javax.annotation.Nonnull; + /** - * Cache instance to supply the weight of a {@link PermissionHolder}. + * Cache instance to supply the weight of a {@link Group}. */ public class WeightCache extends Cache { private final Group group; @@ -45,6 +47,7 @@ public class WeightCache extends Cache { this.group = group; } + @Nonnull @Override protected OptionalInt supply() { boolean seen = false; diff --git a/common/src/main/java/me/lucko/luckperms/common/node/model/NodeTypes.java b/common/src/main/java/me/lucko/luckperms/common/node/model/NodeTypes.java index 4ca128fc..af6632bf 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/model/NodeTypes.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/model/NodeTypes.java @@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableMap; import me.lucko.luckperms.api.nodetype.NodeType; import me.lucko.luckperms.api.nodetype.NodeTypeKey; +import me.lucko.luckperms.api.nodetype.types.DisplayNameType; import me.lucko.luckperms.api.nodetype.types.InheritanceType; import me.lucko.luckperms.api.nodetype.types.MetaType; import me.lucko.luckperms.api.nodetype.types.PrefixType; @@ -51,12 +52,14 @@ public final class NodeTypes { public static final String SUFFIX_KEY = "suffix"; public static final String META_KEY = "meta"; public static final String WEIGHT_KEY = "weight"; + public static final String DISPLAY_NAME_KEY = "displayname"; public static final String GROUP_NODE_MARKER = "group."; public static final String PREFIX_NODE_MARKER = PREFIX_KEY + "."; public static final String SUFFIX_NODE_MARKER = SUFFIX_KEY + "."; public static final String META_NODE_MARKER = META_KEY + "."; public static final String WEIGHT_NODE_MARKER = WEIGHT_KEY + "."; + public static final String DISPLAY_NAME_NODE_MARKER = DISPLAY_NAME_KEY + "."; // used to split prefix/suffix/meta nodes private static final Splitter META_SPLITTER = Splitter.on(PatternCache.compileDelimiterPattern(".", "\\")).limit(2); @@ -89,6 +92,11 @@ public final class NodeTypes { results.put(WeightType.KEY, type); } + type = parseDisplayNameType(s); + if (type != null) { + results.put(DisplayNameType.KEY, type); + } + if (results.isEmpty()) { return ImmutableMap.of(); } @@ -182,6 +190,14 @@ public final class NodeTypes { } } + private static DisplayNameType parseDisplayNameType(String s) { + if (!s.toLowerCase().startsWith(DISPLAY_NAME_NODE_MARKER)) { + return null; + } + + return new DisplayName(s.substring(DISPLAY_NAME_NODE_MARKER.length())); + } + private static final class Inheritance implements InheritanceType { private final String groupName; @@ -408,6 +424,38 @@ public final class NodeTypes { } } + private static final class DisplayName implements DisplayNameType { + private final String displayName; + + private DisplayName(String displayName) { + this.displayName = displayName; + } + + @Nonnull + @Override + public String getDisplayName() { + return this.displayName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DisplayName that = (DisplayName) o; + return Objects.equals(this.displayName, that.displayName); + } + + @Override + public int hashCode() { + return Objects.hash(this.displayName); + } + + @Override + public String toString() { + return "DisplayName{displayName='" + this.displayName + '\'' + '}'; + } + } + private NodeTypes() {} } diff --git a/common/src/main/java/me/lucko/luckperms/common/primarygroup/CachedPrimaryGroupHolder.java b/common/src/main/java/me/lucko/luckperms/common/primarygroup/CachedPrimaryGroupHolder.java index b55f81de..a718754a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/primarygroup/CachedPrimaryGroupHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/primarygroup/CachedPrimaryGroupHolder.java @@ -30,6 +30,8 @@ import me.lucko.luckperms.common.caching.handlers.StateListener; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.factory.NodeFactory; +import javax.annotation.Nonnull; + /** * Abstract implementation of {@link StateListener} which caches all lookups. */ @@ -37,6 +39,7 @@ public abstract class CachedPrimaryGroupHolder extends StoredHolder implements S // cache lookups private final Cache cache = new Cache() { + @Nonnull @Override protected String supply() { return calculateValue();