Refactor meta stacking to be mapped in MetaCache - towards lucko/LuckPermsPlaceholders#1

This commit is contained in:
Luck 2017-05-16 19:11:34 +01:00
parent 99c6fe20c2
commit dc801464ef
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
36 changed files with 764 additions and 870 deletions

View File

@ -41,6 +41,7 @@ import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.common.utils.ProgressLogger;
@ -224,7 +225,8 @@ public class MigrationBPermissions extends SubCommand<Object> {
}
if (meta.getKey().equalsIgnoreCase("prefix") || meta.getKey().equalsIgnoreCase("suffix")) {
holder.setPermission(NodeFactory.makeChatMetaNode(meta.getKey().equalsIgnoreCase("prefix"), c.getPriority(), meta.getValue()).setWorld(world.getName()).build());
MetaType type = MetaType.valueOf(meta.getKey().toUpperCase());
holder.setPermission(NodeFactory.makeChatMetaNode(type, c.getPriority(), meta.getValue()).setWorld(world.getName()).build());
continue;
}

View File

@ -35,6 +35,7 @@ import me.lucko.luckperms.common.commands.impl.migration.MigrationUtils;
import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.common.utils.ProgressLogger;
@ -172,7 +173,8 @@ public class MigrationGroupManager extends SubCommand<Object> {
}
if (key.equals("prefix") || key.equals("suffix")) {
groups.get(groupName).add(NodeFactory.makeChatMetaNode(key.equals("prefix"), 50, value).setWorld(worldMappingFunc.apply(world)).build());
MetaType type = MetaType.valueOf(key.toUpperCase());
groups.get(groupName).add(NodeFactory.makeChatMetaNode(type, 50, value).setWorld(worldMappingFunc.apply(world)).build());
} else {
groups.get(groupName).add(NodeFactory.makeMetaNode(key, value).setWorld(worldMappingFunc.apply(world)).build());
}
@ -228,7 +230,8 @@ public class MigrationGroupManager extends SubCommand<Object> {
}
if (key.equals("prefix") || key.equals("suffix")) {
users.get(uuid).add(NodeFactory.makeChatMetaNode(key.equals("prefix"), 100, value).setWorld(worldMappingFunc.apply(world)).build());
MetaType type = MetaType.valueOf(key.toUpperCase());
users.get(uuid).add(NodeFactory.makeChatMetaNode(type, 100, value).setWorld(worldMappingFunc.apply(world)).build());
} else {
users.get(uuid).add(NodeFactory.makeMetaNode(key, value).setWorld(worldMappingFunc.apply(world)).build());
}

View File

@ -39,6 +39,7 @@ import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.Track;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.common.utils.ProgressLogger;
@ -213,7 +214,8 @@ public class MigrationZPermissions extends SubCommand<Object> {
String key = metadata.getName().toLowerCase();
if (key.equals("prefix") || key.equals("suffix")) {
holder.setPermission(NodeFactory.makeChatMetaNode(key.equals("prefix"), weight, metadata.getStringValue()).build());
MetaType type = MetaType.valueOf(key.toUpperCase());
holder.setPermission(NodeFactory.makeChatMetaNode(type, weight, metadata.getStringValue()).build());
} else {
holder.setPermission(NodeFactory.makeMetaNode(key, metadata.getStringValue()).build());
}

View File

@ -35,6 +35,7 @@ import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import net.milkbowl.vault.chat.Chat;
@ -83,23 +84,23 @@ public class VaultChatHook extends Chat {
});
}
private void setChatMeta(boolean prefix, PermissionHolder holder, String value, String world) {
private void setChatMeta(MetaType type, PermissionHolder holder, String value, String world) {
String finalWorld = perms.isIgnoreWorld() ? null : world;
if (holder == null) return;
if (value.equals("")) return;
perms.log("Setting " + (prefix ? "prefix" : "suffix") + " for " + holder.getObjectName() + " on world " + world + ", server " + perms.getServer());
perms.log("Setting " + type.name().toLowerCase() + " for " + holder.getObjectName() + " on world " + world + ", server " + perms.getServer());
perms.getScheduler().execute(() -> {
// remove all prefixes/suffixes directly set on the user/group
holder.removeIf(n -> prefix ? n.isPrefix() : n.isSuffix());
holder.removeIf(type::matches);
// find the max inherited priority & add 10
MetaAccumulator metaAccumulator = holder.accumulateMeta(null, null, ExtractedContexts.generate(perms.createContextForWorldSet(finalWorld)));
int priority = (prefix ? metaAccumulator.getPrefixes() : metaAccumulator.getSuffixes()).keySet().stream()
int priority = (type == MetaType.PREFIX ? metaAccumulator.getPrefixes() : metaAccumulator.getSuffixes()).keySet().stream()
.mapToInt(e -> e).max().orElse(0) + 10;
Node.Builder chatMetaNode = NodeFactory.makeChatMetaNode(prefix, priority, value);
Node.Builder chatMetaNode = NodeFactory.makeChatMetaNode(type, priority, value);
if (!perms.getServer().equalsIgnoreCase("global")) {
chatMetaNode.setServer(perms.getServer());
}
@ -122,14 +123,14 @@ public class VaultChatHook extends Chat {
return ret != null ? ret : defaultValue;
}
private String getUserChatMeta(boolean prefix, User user, String world) {
private String getUserChatMeta(MetaType type, User user, String world) {
if (user == null) return "";
world = perms.isIgnoreWorld() ? null : world;
perms.log("Getting " + (prefix ? "prefix" : "suffix") + " for user " + user.getFriendlyName() + " on world " + world + ", server " + perms.getServer());
perms.log("Getting " + type.name().toLowerCase() + " for user " + user.getFriendlyName() + " on world " + world + ", server " + perms.getServer());
MetaData data = user.getUserData().getMetaData(perms.createContextForWorldLookup(perms.getPlugin().getPlayer(user), world));
String ret = prefix ? data.getPrefix() : data.getSuffix();
String ret = type == MetaType.PREFIX ? data.getPrefix() : data.getSuffix();
return ret != null ? ret : "";
}
@ -153,11 +154,11 @@ public class VaultChatHook extends Chat {
return defaultValue;
}
private String getGroupChatMeta(boolean prefix, Group group, String world) {
private String getGroupChatMeta(MetaType type, Group group, String world) {
world = perms.isIgnoreWorld() ? null : world;
if (group == null) return "";
perms.log("Getting " + (prefix ? "prefix" : "suffix") + " for group " + group + " on world " + world + ", server " + perms.getServer());
perms.log("Getting " + type.name().toLowerCase() + " for group " + group + " on world " + world + ", server " + perms.getServer());
int priority = Integer.MIN_VALUE;
String meta = null;
@ -165,10 +166,10 @@ public class VaultChatHook extends Chat {
ExtractedContexts ec = ExtractedContexts.generate(Contexts.of(perms.createContextForWorldLookup(world).getContexts(), perms.isIncludeGlobal(), true, true, true, true, false));
for (Node n : group.getAllNodes(ec)) {
if (!n.getValue()) continue;
if (prefix ? !n.isPrefix() : !n.isSuffix()) continue;
if (type.shouldIgnore(n)) continue;
if (!n.shouldApplyWithContext(perms.createContextForWorldLookup(world).getContexts())) continue;
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
Map.Entry<Integer, String> value = type.getEntry(n);
if (value.getKey() > priority) {
meta = value.getValue();
priority = value.getKey();
@ -181,49 +182,49 @@ public class VaultChatHook extends Chat {
@Override
public String getPlayerPrefix(String world, @NonNull String player) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
return getUserChatMeta(true, user, world);
return getUserChatMeta(MetaType.PREFIX, user, world);
}
@Override
public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
setChatMeta(true, user, prefix, world);
setChatMeta(MetaType.PREFIX, user, prefix, world);
}
@Override
public String getPlayerSuffix(String world, @NonNull String player) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
return getUserChatMeta(false, user, world);
return getUserChatMeta(MetaType.SUFFIX, user, world);
}
@Override
public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
setChatMeta(false, user, suffix, world);
setChatMeta(MetaType.SUFFIX, user, suffix, world);
}
@Override
public String getGroupPrefix(String world, @NonNull String group) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
return getGroupChatMeta(true, g, world);
return getGroupChatMeta(MetaType.PREFIX, g, world);
}
@Override
public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
setChatMeta(true, g, prefix, world);
setChatMeta(MetaType.PREFIX, g, prefix, world);
}
@Override
public String getGroupSuffix(String world, @NonNull String group) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
return getGroupChatMeta(false, g, world);
return getGroupChatMeta(MetaType.SUFFIX, g, world);
}
@Override
public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
setChatMeta(false, g, suffix, world);
setChatMeta(MetaType.SUFFIX, g, suffix, world);
}
@Override

View File

@ -27,10 +27,14 @@ package me.lucko.luckperms.common.caching;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.caching.stacking.MetaStack;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.metastacking.MetaStack;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.Comparator;
import java.util.HashMap;
@ -44,6 +48,12 @@ import java.util.TreeMap;
@Getter
@ToString
public class MetaAccumulator {
public static MetaAccumulator makeFromConfig(LuckPermsPlugin plugin) {
return new MetaAccumulator(
plugin.getConfiguration().get(ConfigKeys.PREFIX_FORMATTING_OPTIONS).newStack(MetaType.PREFIX),
plugin.getConfiguration().get(ConfigKeys.SUFFIX_FORMATTING_OPTIONS).newStack(MetaType.SUFFIX)
);
}
@Getter(AccessLevel.NONE)
private final Map<String, String> meta;
@ -54,7 +64,7 @@ public class MetaAccumulator {
private final MetaStack prefixStack;
private final MetaStack suffixStack;
public MetaAccumulator(MetaStack prefixStack, MetaStack suffixStack) {
public MetaAccumulator(@NonNull MetaStack prefixStack, @NonNull MetaStack suffixStack) {
this.meta = new HashMap<>();
this.prefixes = new TreeMap<>(Comparator.reverseOrder());
this.suffixes = new TreeMap<>(Comparator.reverseOrder());
@ -103,4 +113,12 @@ public class MetaAccumulator {
return this.meta;
}
public Map<Integer, String> getChatMeta(MetaType type) {
return type == MetaType.PREFIX ? prefixes : suffixes;
}
public MetaStack getStack(MetaType type) {
return type == MetaType.PREFIX ? prefixStack : suffixStack;
}
}

View File

@ -32,8 +32,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.common.caching.stacking.MetaStack;
import me.lucko.luckperms.common.caching.stacking.NoopMetaStack;
import me.lucko.luckperms.common.metastacking.MetaStack;
import java.util.Map;
import java.util.SortedMap;
@ -57,10 +56,10 @@ public class MetaCache implements MetaData {
private SortedMap<Integer, String> suffixes = ImmutableSortedMap.of();
@Getter
private MetaStack prefixStack = NoopMetaStack.INSTANCE;
private MetaStack prefixStack = null;
@Getter
private MetaStack suffixStack = NoopMetaStack.INSTANCE;
private MetaStack suffixStack = null;
public void loadMeta(MetaAccumulator meta) {
lock.writeLock().lock();
@ -79,7 +78,7 @@ public class MetaCache implements MetaData {
public String getPrefix() {
lock.readLock().lock();
try {
return prefixStack.toFormattedString();
return prefixStack == null ? null : prefixStack.toFormattedString();
} finally {
lock.readLock().unlock();
}
@ -89,7 +88,7 @@ public class MetaCache implements MetaData {
public String getSuffix() {
lock.readLock().lock();
try {
return suffixStack.toFormattedString();
return suffixStack == null ? null : suffixStack.toFormattedString();
} finally {
lock.readLock().unlock();
}

View File

@ -0,0 +1,67 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.caching;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.metastacking.definition.MetaStackDefinition;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@Getter
@ToString
@EqualsAndHashCode
public final class MetaContexts {
public static MetaContexts makeFromConfig(Contexts contexts, LuckPermsPlugin plugin) {
return new MetaContexts(
contexts,
plugin.getConfiguration().get(ConfigKeys.PREFIX_FORMATTING_OPTIONS),
plugin.getConfiguration().get(ConfigKeys.SUFFIX_FORMATTING_OPTIONS)
);
}
private final Contexts contexts;
private final MetaStackDefinition prefixStackDefinition;
private final MetaStackDefinition suffixStackDefinition;
public MetaContexts(@NonNull Contexts contexts, @NonNull MetaStackDefinition prefixStackDefinition, @NonNull MetaStackDefinition suffixStackDefinition) {
this.contexts = contexts;
this.prefixStackDefinition = prefixStackDefinition;
this.suffixStackDefinition = suffixStackDefinition;
}
public MetaAccumulator newAccumulator() {
return new MetaAccumulator(
prefixStackDefinition.newStack(MetaType.PREFIX),
suffixStackDefinition.newStack(MetaType.SUFFIX)
);
}
}

View File

@ -69,17 +69,17 @@ public class UserCache implements UserData {
}
});
private final LoadingCache<Contexts, MetaCache> meta = Caffeine.newBuilder()
private final LoadingCache<MetaContexts, MetaCache> meta = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(new CacheLoader<Contexts, MetaCache>() {
.build(new CacheLoader<MetaContexts, MetaCache>() {
@Override
public MetaCache load(Contexts contexts) {
public MetaCache load(MetaContexts contexts) {
return calculateMeta(contexts);
}
@Override
public MetaCache reload(Contexts contexts, MetaCache oldData) {
oldData.loadMeta(user.accumulateMeta(null, null, ExtractedContexts.generate(contexts)));
public MetaCache reload(MetaContexts contexts, MetaCache oldData) {
oldData.loadMeta(user.accumulateMeta(contexts.newAccumulator(), null, ExtractedContexts.generate(contexts.getContexts())));
return oldData;
}
});
@ -91,6 +91,11 @@ public class UserCache implements UserData {
@Override
public MetaData getMetaData(@NonNull Contexts contexts) {
// just create a MetaContexts instance using the values in the config
return getMetaData(MetaContexts.makeFromConfig(contexts, user.getPlugin()));
}
public MetaData getMetaData(@NonNull MetaContexts contexts) {
return meta.get(contexts);
}
@ -103,8 +108,13 @@ public class UserCache implements UserData {
@Override
public MetaCache calculateMeta(@NonNull Contexts contexts) {
// just create a MetaContexts instance using the values in the config
return calculateMeta(MetaContexts.makeFromConfig(contexts, user.getPlugin()));
}
public MetaCache calculateMeta(@NonNull MetaContexts contexts) {
MetaCache data = new MetaCache();
data.loadMeta(user.accumulateMeta(null, null, ExtractedContexts.generate(contexts)));
data.loadMeta(user.accumulateMeta(contexts.newAccumulator(), null, ExtractedContexts.generate(contexts.getContexts())));
return data;
}
@ -115,6 +125,10 @@ public class UserCache implements UserData {
@Override
public void recalculateMeta(@NonNull Contexts contexts) {
recalculateMeta(MetaContexts.makeFromConfig(contexts, user.getPlugin()));
}
public void recalculateMeta(@NonNull MetaContexts contexts) {
meta.refresh(contexts);
}
@ -126,7 +140,7 @@ public class UserCache implements UserData {
@Override
public void recalculateMeta() {
Set<Contexts> keys = ImmutableSet.copyOf(meta.asMap().keySet());
Set<MetaContexts> keys = ImmutableSet.copyOf(meta.asMap().keySet());
keys.forEach(meta::refresh);
}
@ -138,7 +152,7 @@ public class UserCache implements UserData {
@Override
public void preCalculate(@NonNull Contexts contexts) {
permission.get(contexts);
meta.get(contexts);
meta.get(MetaContexts.makeFromConfig(contexts, user.getPlugin()));
}
public void invalidateCache() {

View File

@ -1,113 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.caching.stacking;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.common.core.model.Track;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public interface MetaStackElement {
Optional<Map.Entry<Integer, String>> getEntry();
boolean accumulateNode(LocalizedNode node);
MetaStackElement copy();
/**
* Returns true if the types do not match
* @param expectingPrefix if the method is expecting a prefix
* @param node the node to check
* @return true if the accumulation should return
*/
static boolean checkMetaType(boolean expectingPrefix, Node node) {
if (expectingPrefix) {
if (!node.isPrefix()) {
return true;
}
} else {
if (!node.isSuffix()) {
return true;
}
}
return false;
}
/**
* Returns true if the node is not held by a user
* @param node the node to check
* @return true if the accumulation should return
*/
static boolean checkOwnElement(LocalizedNode node) {
if (node.getLocation() == null || node.getLocation().equals("")) {
return true;
}
try {
UUID.fromString(node.getLocation());
return false;
} catch (IllegalArgumentException e) {
return true;
}
}
/**
* Returns true if the node is not held by a group on the track
* @param node the node to check
* @param track the track
* @return true if the accumulation should return
*/
static boolean checkTrackElement(LuckPermsPlugin plugin, LocalizedNode node, String track) {
if (node.getLocation() == null || node.getLocation().equals("")) {
return true;
}
Track t = plugin.getTrackManager().getIfLoaded(track);
return t == null || !t.containsGroup(node.getLocation());
}
/**
* Returns true if the node is held by a group on the track
* @param node the node to check
* @param track the track
* @return true if the accumulation should return
*/
static boolean checkNotTrackElement(LuckPermsPlugin plugin, LocalizedNode node, String track) {
// it's not come from a group on this track (from the user directly)
if (node.getLocation() == null || node.getLocation().equals("")) {
return false;
}
Track t = plugin.getTrackManager().getIfLoaded(track);
return t == null || t.containsGroup(node.getLocation());
}
}

View File

@ -1,94 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.caching.stacking;
import lombok.experimental.UtilityClass;
import me.lucko.luckperms.common.caching.stacking.elements.HighestPriorityElement;
import me.lucko.luckperms.common.caching.stacking.elements.HighestPriorityNotOnTrackElement;
import me.lucko.luckperms.common.caching.stacking.elements.HighestPriorityOwnElement;
import me.lucko.luckperms.common.caching.stacking.elements.HighestPriorityTrackElement;
import me.lucko.luckperms.common.caching.stacking.elements.LowestPriorityElement;
import me.lucko.luckperms.common.caching.stacking.elements.LowestPriorityNotOnTrackElement;
import me.lucko.luckperms.common.caching.stacking.elements.LowestPriorityOwnElement;
import me.lucko.luckperms.common.caching.stacking.elements.LowestPriorityTrackElement;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import java.util.List;
import java.util.Optional;
@UtilityClass
public class StackElementFactory {
public static Optional<MetaStackElement> fromString(LuckPermsPlugin plugin, String s, boolean prefix) {
s = s.toLowerCase();
if (s.equals("highest")) {
return Optional.of(new HighestPriorityElement(prefix));
}
if (s.equals("lowest")) {
return Optional.of(new LowestPriorityElement(prefix));
}
if (s.equals("highest_own")) {
return Optional.of(new HighestPriorityOwnElement(prefix));
}
if (s.equals("lowest_own")) {
return Optional.of(new LowestPriorityOwnElement(prefix));
}
if (s.startsWith("highest_on_track_") && s.length() > "highest_on_track_".length()) {
String track = s.substring("highest_on_track_".length());
return Optional.of(new HighestPriorityTrackElement(prefix, plugin, track));
}
if (s.startsWith("lowest_on_track_") && s.length() > "lowest_on_track_".length()) {
String track = s.substring("lowest_on_track_".length());
return Optional.of(new LowestPriorityTrackElement(prefix, plugin, track));
}
if (s.startsWith("highest_not_on_track_") && s.length() > "highest_not_on_track_".length()) {
String track = s.substring("highest_not_on_track_".length());
return Optional.of(new HighestPriorityNotOnTrackElement(prefix, plugin, track));
}
if (s.startsWith("lowest_not_on_track_") && s.length() > "lowest_not_on_track_".length()) {
String track = s.substring("lowest_not_on_track_".length());
return Optional.of(new LowestPriorityNotOnTrackElement(prefix, plugin, track));
}
new IllegalArgumentException("Cannot parse MetaStackElement: " + s).printStackTrace();
return Optional.empty();
}
public static List<MetaStackElement> fromList(LuckPermsPlugin plugin, List<String> strings, boolean prefix) {
return strings.stream().map(s -> fromString(plugin, s, prefix)).filter(Optional::isPresent).map(Optional::get).collect(ImmutableCollectors.toImmutableList());
}
}

View File

@ -1,76 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.caching.stacking.elements;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.caching.stacking.MetaStackElement;
import java.util.Map;
import java.util.Optional;
@RequiredArgsConstructor
public class HighestPriorityElement implements MetaStackElement {
/**
* Returns true if the current node has the greater priority
* @param current the current entry
* @param newEntry the new entry
* @return true if the accumulation should return
*/
public static boolean compareEntries(Map.Entry<Integer, String> current, Map.Entry<Integer, String> newEntry) {
return current != null && current.getKey() >= newEntry.getKey();
}
private final boolean prefix;
private Map.Entry<Integer, String> entry = null;
@Override
public Optional<Map.Entry<Integer, String>> getEntry() {
return Optional.ofNullable(entry);
}
@Override
public boolean accumulateNode(LocalizedNode node) {
if (MetaStackElement.checkMetaType(prefix, node)) {
return false;
}
Map.Entry<Integer, String> entry = prefix ? node.getPrefix() : node.getSuffix();
if (compareEntries(this.entry, entry)) {
return false;
}
this.entry = entry;
return true;
}
@Override
public MetaStackElement copy() {
return new HighestPriorityElement(prefix);
}
}

View File

@ -1,73 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.caching.stacking.elements;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.caching.stacking.MetaStackElement;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.Map;
import java.util.Optional;
@RequiredArgsConstructor
public class HighestPriorityNotOnTrackElement implements MetaStackElement {
private final boolean prefix;
private final LuckPermsPlugin plugin;
private final String trackName;
private Map.Entry<Integer, String> entry = null;
@Override
public Optional<Map.Entry<Integer, String>> getEntry() {
return Optional.ofNullable(entry);
}
@Override
public boolean accumulateNode(LocalizedNode node) {
if (MetaStackElement.checkMetaType(prefix, node)) {
return false;
}
Map.Entry<Integer, String> entry = prefix ? node.getPrefix() : node.getSuffix();
if (HighestPriorityElement.compareEntries(this.entry, entry)) {
return false;
}
if (MetaStackElement.checkNotTrackElement(plugin, node, trackName)) {
return false;
}
this.entry = entry;
return true;
}
@Override
public MetaStackElement copy() {
return new HighestPriorityNotOnTrackElement(prefix, plugin, trackName);
}
}

View File

@ -1,73 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.caching.stacking.elements;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.caching.stacking.MetaStackElement;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.Map;
import java.util.Optional;
@RequiredArgsConstructor
public class HighestPriorityTrackElement implements MetaStackElement {
private final boolean prefix;
private final LuckPermsPlugin plugin;
private final String trackName;
private Map.Entry<Integer, String> entry = null;
@Override
public Optional<Map.Entry<Integer, String>> getEntry() {
return Optional.ofNullable(entry);
}
@Override
public boolean accumulateNode(LocalizedNode node) {
if (MetaStackElement.checkMetaType(prefix, node)) {
return false;
}
Map.Entry<Integer, String> entry = prefix ? node.getPrefix() : node.getSuffix();
if (HighestPriorityElement.compareEntries(this.entry, entry)) {
return false;
}
if (MetaStackElement.checkTrackElement(plugin, node, trackName)) {
return false;
}
this.entry = entry;
return true;
}
@Override
public MetaStackElement copy() {
return new HighestPriorityTrackElement(prefix, plugin, trackName);
}
}

View File

@ -1,76 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.caching.stacking.elements;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.caching.stacking.MetaStackElement;
import java.util.Map;
import java.util.Optional;
@RequiredArgsConstructor
public class LowestPriorityElement implements MetaStackElement {
/**
* Returns true if the current node has the lesser priority
* @param current the current entry
* @param newEntry the new entry
* @return true if the accumulation should return
*/
public static boolean compareEntries(Map.Entry<Integer, String> current, Map.Entry<Integer, String> newEntry) {
return current != null && current.getKey() <= newEntry.getKey();
}
private final boolean prefix;
private Map.Entry<Integer, String> entry = null;
@Override
public Optional<Map.Entry<Integer, String>> getEntry() {
return Optional.ofNullable(entry);
}
@Override
public boolean accumulateNode(LocalizedNode node) {
if (MetaStackElement.checkMetaType(prefix, node)) {
return false;
}
Map.Entry<Integer, String> entry = prefix ? node.getPrefix() : node.getSuffix();
if (compareEntries(this.entry, entry)) {
return false;
}
this.entry = entry;
return true;
}
@Override
public MetaStackElement copy() {
return new LowestPriorityElement(prefix);
}
}

View File

@ -1,73 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.caching.stacking.elements;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.caching.stacking.MetaStackElement;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.Map;
import java.util.Optional;
@RequiredArgsConstructor
public class LowestPriorityNotOnTrackElement implements MetaStackElement {
private final boolean prefix;
private final LuckPermsPlugin plugin;
private final String trackName;
private Map.Entry<Integer, String> entry = null;
@Override
public Optional<Map.Entry<Integer, String>> getEntry() {
return Optional.ofNullable(entry);
}
@Override
public boolean accumulateNode(LocalizedNode node) {
if (MetaStackElement.checkMetaType(prefix, node)) {
return false;
}
Map.Entry<Integer, String> entry = prefix ? node.getPrefix() : node.getSuffix();
if (LowestPriorityElement.compareEntries(this.entry, entry)) {
return false;
}
if (MetaStackElement.checkNotTrackElement(plugin, node, trackName)) {
return false;
}
this.entry = entry;
return true;
}
@Override
public MetaStackElement copy() {
return new LowestPriorityNotOnTrackElement(prefix, plugin, trackName);
}
}

View File

@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.common.commands.abstraction.SharedMainCommand;
import me.lucko.luckperms.common.commands.abstraction.SharedSubCommand;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.metastacking.MetaType;
public class CommandMeta<T extends PermissionHolder> extends SharedMainCommand<T> {
public CommandMeta(boolean user) {
@ -39,14 +40,14 @@ public class CommandMeta<T extends PermissionHolder> extends SharedMainCommand<T
.add(new MetaUnset())
.add(new MetaSetTemp())
.add(new MetaUnsetTemp())
.add(new MetaAddChatMeta(true))
.add(new MetaAddChatMeta(false))
.add(new MetaRemoveChatMeta(true))
.add(new MetaRemoveChatMeta(false))
.add(new MetaAddTempChatMeta(true))
.add(new MetaAddTempChatMeta(false))
.add(new MetaRemoveTempChatMeta(true))
.add(new MetaRemoveTempChatMeta(false))
.add(new MetaAddChatMeta(MetaType.PREFIX))
.add(new MetaAddChatMeta(MetaType.SUFFIX))
.add(new MetaRemoveChatMeta(MetaType.PREFIX))
.add(new MetaRemoveChatMeta(MetaType.SUFFIX))
.add(new MetaAddTempChatMeta(MetaType.PREFIX))
.add(new MetaAddTempChatMeta(MetaType.SUFFIX))
.add(new MetaRemoveTempChatMeta(MetaType.PREFIX))
.add(new MetaRemoveTempChatMeta(MetaType.SUFFIX))
.add(new MetaClear())
.build());
}

View File

@ -39,30 +39,29 @@ import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.data.LogEntry;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class MetaAddChatMeta extends SharedSubCommand {
private static final Function<Boolean, String> DESCRIPTOR = b -> b ? "prefix" : "suffix";
private final boolean isPrefix;
private final MetaType type;
public MetaAddChatMeta(boolean isPrefix) {
super("add" + DESCRIPTOR.apply(isPrefix),
"Adds a " + DESCRIPTOR.apply(isPrefix),
isPrefix ? Permission.USER_META_ADDPREFIX : Permission.USER_META_ADDSUFFIX,
isPrefix ? Permission.GROUP_META_ADDPREFIX : Permission.GROUP_META_ADDSUFFIX,
public MetaAddChatMeta(MetaType type) {
super("add" + type.name().toLowerCase(),
"Adds a " + type.name().toLowerCase(),
type == MetaType.PREFIX ? Permission.USER_META_ADDPREFIX : Permission.USER_META_ADDSUFFIX,
type == MetaType.PREFIX ? Permission.GROUP_META_ADDPREFIX : Permission.GROUP_META_ADDSUFFIX,
Predicates.inRange(0, 1),
Arg.list(
Arg.create("priority", true, "the priority to add the " + DESCRIPTOR.apply(isPrefix) + " at"),
Arg.create(DESCRIPTOR.apply(isPrefix), true, "the " + DESCRIPTOR.apply(isPrefix) + " string"),
Arg.create("context...", false, "the contexts to add the " + DESCRIPTOR.apply(isPrefix) + " in")
Arg.create("priority", true, "the priority to add the " + type.name().toLowerCase() + " at"),
Arg.create(type.name().toLowerCase(), true, "the " + type.name().toLowerCase() + " string"),
Arg.create("context...", false, "the contexts to add the " + type.name().toLowerCase() + " in")
)
);
this.isPrefix = isPrefix;
this.type = type;
}
@Override
@ -71,18 +70,18 @@ public class MetaAddChatMeta extends SharedSubCommand {
String meta = ArgumentUtils.handleString(1, args);
MutableContextSet context = ArgumentUtils.handleContext(2, args, plugin);
DataMutateResult result = holder.setPermission(NodeFactory.makeChatMetaNode(isPrefix, priority, meta).withExtraContext(context).build());
DataMutateResult result = holder.setPermission(NodeFactory.makeChatMetaNode(type, priority, meta).withExtraContext(context).build());
if (result.asBoolean()) {
Message.ADD_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix), meta, priority, Util.contextSetToString(context));
Message.ADD_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), type.name().toLowerCase(), meta, priority, Util.contextSetToString(context));
LogEntry.build().actor(sender).acted(holder)
.action("meta add" + DESCRIPTOR.apply(isPrefix) + " " + args.stream().map(ArgumentUtils.WRAPPER).collect(Collectors.joining(" ")))
.action("meta add" + type.name().toLowerCase() + " " + args.stream().map(ArgumentUtils.WRAPPER).collect(Collectors.joining(" ")))
.build().submit(plugin, sender);
save(holder, sender, plugin);
return CommandResult.SUCCESS;
} else {
Message.ALREADY_HAS_CHAT_META.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix));
Message.ALREADY_HAS_CHAT_META.send(sender, holder.getFriendlyName(), type.name().toLowerCase());
return CommandResult.STATE_ERROR;
}
}

View File

@ -42,33 +42,32 @@ import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.TemporaryModifier;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.data.LogEntry;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.DateUtil;
import me.lucko.luckperms.common.utils.Predicates;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class MetaAddTempChatMeta extends SharedSubCommand {
private static final Function<Boolean, String> DESCRIPTOR = b -> b ? "prefix" : "suffix";
private final boolean isPrefix;
private final MetaType type;
public MetaAddTempChatMeta(boolean isPrefix) {
super("addtemp" + DESCRIPTOR.apply(isPrefix),
"Adds a " + DESCRIPTOR.apply(isPrefix) + " temporarily",
isPrefix ? Permission.USER_META_ADDTEMP_PREFIX : Permission.USER_META_ADDTEMP_SUFFIX,
isPrefix ? Permission.GROUP_META_ADDTEMP_PREFIX : Permission.GROUP_META_ADDTEMP_SUFFIX,
public MetaAddTempChatMeta(MetaType type) {
super("addtemp" + type.name().toLowerCase(),
"Adds a " + type.name().toLowerCase() + " temporarily",
type == MetaType.PREFIX ? Permission.USER_META_ADDTEMP_PREFIX : Permission.USER_META_ADDTEMP_SUFFIX,
type == MetaType.PREFIX ? Permission.GROUP_META_ADDTEMP_PREFIX : Permission.GROUP_META_ADDTEMP_SUFFIX,
Predicates.inRange(0, 2),
Arg.list(
Arg.create("priority", true, "the priority to add the " + DESCRIPTOR.apply(isPrefix) + " at"),
Arg.create(DESCRIPTOR.apply(isPrefix), true, "the " + DESCRIPTOR.apply(isPrefix) + " string"),
Arg.create("duration", true, "the duration until the " + DESCRIPTOR.apply(isPrefix) + " expires"),
Arg.create("context...", false, "the contexts to add the " + DESCRIPTOR.apply(isPrefix) + " in")
Arg.create("priority", true, "the priority to add the " + type.name().toLowerCase() + " at"),
Arg.create(type.name().toLowerCase(), true, "the " + type.name().toLowerCase() + " string"),
Arg.create("duration", true, "the duration until the " + type.name().toLowerCase() + " expires"),
Arg.create("context...", false, "the contexts to add the " + type.name().toLowerCase() + " in")
)
);
this.isPrefix = isPrefix;
this.type = type;
}
@Override
@ -79,21 +78,21 @@ public class MetaAddTempChatMeta extends SharedSubCommand {
MutableContextSet context = ArgumentUtils.handleContext(3, args, plugin);
TemporaryModifier modifier = plugin.getConfiguration().get(ConfigKeys.TEMPORARY_ADD_BEHAVIOUR);
Map.Entry<DataMutateResult, Node> ret = holder.setPermission(NodeFactory.makeChatMetaNode(isPrefix, priority, meta).setExpiry(duration).withExtraContext(context).build(), modifier);
Map.Entry<DataMutateResult, Node> ret = holder.setPermission(NodeFactory.makeChatMetaNode(type, priority, meta).setExpiry(duration).withExtraContext(context).build(), modifier);
if (ret.getKey().asBoolean()) {
duration = ret.getValue().getExpiryUnixTime();
Message.ADD_TEMP_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix), meta, priority, DateUtil.formatDateDiff(duration), Util.contextSetToString(context));
Message.ADD_TEMP_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), type.name().toLowerCase(), meta, priority, DateUtil.formatDateDiff(duration), Util.contextSetToString(context));
LogEntry.build().actor(sender).acted(holder)
.action("meta addtemp" + DESCRIPTOR.apply(isPrefix) + " " + args.stream().map(ArgumentUtils.WRAPPER).collect(Collectors.joining(" ")))
.action("meta addtemp" + type.name().toLowerCase() + " " + args.stream().map(ArgumentUtils.WRAPPER).collect(Collectors.joining(" ")))
.build().submit(plugin, sender);
save(holder, sender, plugin);
return CommandResult.SUCCESS;
} else {
Message.ALREADY_HAS_CHAT_META.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix));
Message.ALREADY_HAS_CHAT_META.send(sender, holder.getFriendlyName(), type.name().toLowerCase());
return CommandResult.STATE_ERROR;
}
}

View File

@ -39,30 +39,29 @@ import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.data.LogEntry;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class MetaRemoveChatMeta extends SharedSubCommand {
private static final Function<Boolean, String> DESCRIPTOR = b -> b ? "prefix" : "suffix";
private final boolean isPrefix;
private final MetaType type;
public MetaRemoveChatMeta(boolean isPrefix) {
super("remove" + DESCRIPTOR.apply(isPrefix),
"Removes a " + DESCRIPTOR.apply(isPrefix),
isPrefix ? Permission.USER_META_REMOVEPREFIX : Permission.USER_META_REMOVESUFFIX,
isPrefix ? Permission.GROUP_META_REMOVEPREFIX : Permission.GROUP_META_REMOVESUFFIX,
public MetaRemoveChatMeta(MetaType type) {
super("remove" + type.name().toLowerCase(),
"Removes a " + type.name().toLowerCase(),
type == MetaType.PREFIX ? Permission.USER_META_REMOVEPREFIX : Permission.USER_META_REMOVESUFFIX,
type == MetaType.PREFIX ? Permission.GROUP_META_REMOVEPREFIX : Permission.GROUP_META_REMOVESUFFIX,
Predicates.is(0),
Arg.list(
Arg.create("priority", true, "the priority to remove the " + DESCRIPTOR.apply(isPrefix) + " at"),
Arg.create(DESCRIPTOR.apply(isPrefix), false, "the " + DESCRIPTOR.apply(isPrefix) + " string"),
Arg.create("context...", false, "the contexts to remove the " + DESCRIPTOR.apply(isPrefix) + " in")
Arg.create("priority", true, "the priority to remove the " + type.name().toLowerCase() + " at"),
Arg.create(type.name().toLowerCase(), false, "the " + type.name().toLowerCase() + " string"),
Arg.create("context...", false, "the contexts to remove the " + type.name().toLowerCase() + " in")
)
);
this.isPrefix = isPrefix;
this.type = type;
}
@Override
@ -74,29 +73,29 @@ public class MetaRemoveChatMeta extends SharedSubCommand {
// Handle bulk removal
if (meta.equalsIgnoreCase("null") || meta.equals("*")) {
holder.removeIf(n ->
(isPrefix ? n.isPrefix() : n.isSuffix()) &&
(isPrefix ? n.getPrefix() : n.getSuffix()).getKey() == priority &&
type.matches(n) &&
type.getEntry(n).getKey() == priority &&
!n.isTemporary() &&
n.getFullContexts().makeImmutable().equals(context.makeImmutable())
);
Message.BULK_REMOVE_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix), priority, Util.contextSetToString(context));
Message.BULK_REMOVE_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), type.name().toLowerCase(), priority, Util.contextSetToString(context));
save(holder, sender, plugin);
return CommandResult.SUCCESS;
}
DataMutateResult result = holder.unsetPermission(NodeFactory.makeChatMetaNode(isPrefix, priority, meta).withExtraContext(context).build());
DataMutateResult result = holder.unsetPermission(NodeFactory.makeChatMetaNode(type, priority, meta).withExtraContext(context).build());
if (result.asBoolean()) {
Message.REMOVE_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix), meta, priority, Util.contextSetToString(context));
Message.REMOVE_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), type.name().toLowerCase(), meta, priority, Util.contextSetToString(context));
LogEntry.build().actor(sender).acted(holder)
.action("meta remove" + DESCRIPTOR.apply(isPrefix) + " " + args.stream().map(ArgumentUtils.WRAPPER).collect(Collectors.joining(" ")))
.action("meta remove" + type.name().toLowerCase() + " " + args.stream().map(ArgumentUtils.WRAPPER).collect(Collectors.joining(" ")))
.build().submit(plugin, sender);
save(holder, sender, plugin);
return CommandResult.SUCCESS;
} else {
Message.DOES_NOT_HAVE_CHAT_META.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix));
Message.DOES_NOT_HAVE_CHAT_META.send(sender, holder.getFriendlyName(), type.name().toLowerCase());
return CommandResult.STATE_ERROR;
}
}

View File

@ -39,30 +39,29 @@ import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.data.LogEntry;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class MetaRemoveTempChatMeta extends SharedSubCommand {
private static final Function<Boolean, String> DESCRIPTOR = b -> b ? "prefix" : "suffix";
private final boolean isPrefix;
private final MetaType type;
public MetaRemoveTempChatMeta(boolean isPrefix) {
super("removetemp" + DESCRIPTOR.apply(isPrefix),
"Removes a temporary " + DESCRIPTOR.apply(isPrefix),
isPrefix ? Permission.USER_META_REMOVETEMP_PREFIX : Permission.USER_META_REMOVETEMP_SUFFIX,
isPrefix ? Permission.GROUP_META_REMOVETEMP_PREFIX : Permission.GROUP_META_REMOVETEMP_SUFFIX,
public MetaRemoveTempChatMeta(MetaType type) {
super("removetemp" +type.name().toLowerCase(),
"Removes a temporary " + type.name().toLowerCase(),
type == MetaType.PREFIX ? Permission.USER_META_REMOVETEMP_PREFIX : Permission.USER_META_REMOVETEMP_SUFFIX,
type == MetaType.PREFIX ? Permission.GROUP_META_REMOVETEMP_PREFIX : Permission.GROUP_META_REMOVETEMP_SUFFIX,
Predicates.is(0),
Arg.list(
Arg.create("priority", true, "the priority to remove the " + DESCRIPTOR.apply(isPrefix) + " at"),
Arg.create(DESCRIPTOR.apply(isPrefix), false, "the " + DESCRIPTOR.apply(isPrefix) + " string"),
Arg.create("context...", false, "the contexts to remove the " + DESCRIPTOR.apply(isPrefix) + " in")
Arg.create("priority", true, "the priority to remove the " + type.name().toLowerCase() + " at"),
Arg.create(type.name().toLowerCase(), false, "the " +type.name().toLowerCase() + " string"),
Arg.create("context...", false, "the contexts to remove the " + type.name().toLowerCase() + " in")
)
);
this.isPrefix = isPrefix;
this.type = type;
}
@Override
@ -74,29 +73,29 @@ public class MetaRemoveTempChatMeta extends SharedSubCommand {
// Handle bulk removal
if (meta.equalsIgnoreCase("null") || meta.equals("*")) {
holder.removeIf(n ->
(isPrefix ? n.isPrefix() : n.isSuffix()) &&
(isPrefix ? n.getPrefix() : n.getSuffix()).getKey() == priority &&
type.matches(n) &&
type.getEntry(n).getKey() == priority &&
!n.isPermanent() &&
n.getFullContexts().makeImmutable().equals(context.makeImmutable())
);
Message.BULK_REMOVE_TEMP_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix), priority, Util.contextSetToString(context));
Message.BULK_REMOVE_TEMP_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), type.name().toLowerCase(), priority, Util.contextSetToString(context));
save(holder, sender, plugin);
return CommandResult.SUCCESS;
}
DataMutateResult result = holder.unsetPermission(NodeFactory.makeChatMetaNode(isPrefix, priority, meta).setExpiry(10L).withExtraContext(context).build());
DataMutateResult result = holder.unsetPermission(NodeFactory.makeChatMetaNode(type, priority, meta).setExpiry(10L).withExtraContext(context).build());
if (result.asBoolean()) {
Message.REMOVE_TEMP_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix), meta, priority, Util.contextSetToString(context));
Message.REMOVE_TEMP_CHATMETA_SUCCESS.send(sender, holder.getFriendlyName(), type.name().toLowerCase(), meta, priority, Util.contextSetToString(context));
LogEntry.build().actor(sender).acted(holder)
.action("meta removetemp" + DESCRIPTOR.apply(isPrefix) + " " + args.stream().map(ArgumentUtils.WRAPPER).collect(Collectors.joining(" ")))
.action("meta removetemp" + type.name().toLowerCase() + " " + args.stream().map(ArgumentUtils.WRAPPER).collect(Collectors.joining(" ")))
.build().submit(plugin, sender);
save(holder, sender, plugin);
return CommandResult.SUCCESS;
} else {
Message.DOES_NOT_HAVE_CHAT_META.send(sender, holder.getFriendlyName(), DESCRIPTOR.apply(isPrefix));
Message.DOES_NOT_HAVE_CHAT_META.send(sender, holder.getFriendlyName(), type.name().toLowerCase());
return CommandResult.STATE_ERROR;
}
}

View File

@ -30,8 +30,6 @@ import lombok.experimental.UtilityClass;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.common.caching.stacking.GenericMetaStack;
import me.lucko.luckperms.common.caching.stacking.StackElementFactory;
import me.lucko.luckperms.common.config.keys.AbstractKey;
import me.lucko.luckperms.common.config.keys.BooleanKey;
import me.lucko.luckperms.common.config.keys.EnduringKey;
@ -43,6 +41,8 @@ import me.lucko.luckperms.common.config.keys.StringKey;
import me.lucko.luckperms.common.core.TemporaryModifier;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.defaults.Rule;
import me.lucko.luckperms.common.metastacking.definition.MetaStackDefinition;
import me.lucko.luckperms.common.metastacking.definition.StandardStackElements;
import me.lucko.luckperms.common.primarygroup.AllParentsByWeightHolder;
import me.lucko.luckperms.common.primarygroup.ParentsByWeightHolder;
import me.lucko.luckperms.common.primarygroup.PrimaryGroupHolder;
@ -216,9 +216,9 @@ public class ConfigKeys {
});
/**
* Creates a new prefix MetaStack element based upon the configured values. Be aware that this instance should be copied for each unique user.
* Creates a new prefix MetaStack element based upon the configured values.
*/
public static final ConfigKey<GenericMetaStack> PREFIX_FORMATTING_OPTIONS = AbstractKey.of(l -> {
public static final ConfigKey<MetaStackDefinition> PREFIX_FORMATTING_OPTIONS = AbstractKey.of(l -> {
List<String> format = l.getList("meta-formatting.prefix.format", new ArrayList<>());
if (format.isEmpty()) {
format.add("highest");
@ -227,13 +227,13 @@ public class ConfigKeys {
String middleSpacer = l.getString("meta-formatting.prefix.middle-spacer", " ");
String endSpacer = l.getString("meta-formatting.prefix.end-spacer", "");
return new GenericMetaStack(StackElementFactory.fromList(l.getPlugin(), format, true), startSpacer, middleSpacer, endSpacer);
return MetaStackDefinition.create(StandardStackElements.parseList(l.getPlugin(), format), startSpacer, middleSpacer, endSpacer);
});
/**
* Creates a new suffix MetaStack element based upon the configured values. Be aware that this instance should be copied for each unique user.
* Creates a new suffix MetaStack element based upon the configured values.
*/
public static final ConfigKey<GenericMetaStack> SUFFIX_FORMATTING_OPTIONS = AbstractKey.of(l -> {
public static final ConfigKey<MetaStackDefinition> SUFFIX_FORMATTING_OPTIONS = AbstractKey.of(l -> {
List<String> format = l.getList("meta-formatting.suffix.format", new ArrayList<>());
if (format.isEmpty()) {
format.add("highest");
@ -242,7 +242,7 @@ public class ConfigKeys {
String middleSpacer = l.getString("meta-formatting.suffix.middle-spacer", " ");
String endSpacer = l.getString("meta-formatting.suffix.end-spacer", "");
return new GenericMetaStack(StackElementFactory.fromList(l.getPlugin(), format, false), startSpacer, middleSpacer, endSpacer);
return MetaStackDefinition.create(StandardStackElements.parseList(l.getPlugin(), format), startSpacer, middleSpacer, endSpacer);
});
/**

View File

@ -35,6 +35,7 @@ import me.lucko.luckperms.api.MetaUtils;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.utils.PatternCache;
import java.util.List;
@ -122,8 +123,8 @@ public class NodeFactory {
return new NodeBuilder("meta." + MetaUtils.escapeCharacters(key) + "." + MetaUtils.escapeCharacters(value));
}
public static Node.Builder makeChatMetaNode(boolean prefix, int priority, String s) {
return prefix ? makePrefixNode(priority, s) : makeSuffixNode(priority, s);
public static Node.Builder makeChatMetaNode(MetaType type, int priority, String s) {
return type == MetaType.PREFIX ? makePrefixNode(priority, s) : makeSuffixNode(priority, s);
}
public static Node.Builder makePrefixNode(int priority, String prefix) {

View File

@ -663,10 +663,7 @@ public abstract class PermissionHolder {
public MetaAccumulator accumulateMeta(MetaAccumulator accumulator, Set<String> excludedGroups, ExtractedContexts context) {
if (accumulator == null) {
accumulator = new MetaAccumulator(
plugin.getConfiguration().get(ConfigKeys.PREFIX_FORMATTING_OPTIONS).copy(),
plugin.getConfiguration().get(ConfigKeys.SUFFIX_FORMATTING_OPTIONS).copy()
);
accumulator = MetaAccumulator.makeFromConfig(plugin);
}
if (excludedGroups == null) {

View File

@ -23,28 +23,38 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.caching.stacking;
package me.lucko.luckperms.common.metastacking;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.metastacking.definition.MetaStackDefinition;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import java.util.ArrayList;
import java.util.List;
@Getter
@RequiredArgsConstructor
public class GenericMetaStack implements MetaStack {
private final List<MetaStackElement> elements;
private final String startSpacer;
private final String middleSpacer;
private final String endSpacer;
private final MetaStackDefinition definition;
private final MetaType targetType;
@Getter(AccessLevel.NONE)
private final List<MetaStackEntry> entries;
public GenericMetaStack(MetaStackDefinition definition, MetaType targetType) {
this.definition = definition;
this.targetType = targetType;
this.entries = definition.getElements().stream()
.map(element -> new SimpleMetaStackEntry(this, element, targetType))
.collect(ImmutableCollectors.toImmutableList());
}
@Override
public String toFormattedString() {
List<MetaStackElement> ret = new ArrayList<>(elements);
List<MetaStackEntry> ret = new ArrayList<>(entries);
ret.removeIf(m -> !m.getEntry().isPresent());
if (ret.isEmpty()) {
@ -52,27 +62,22 @@ public class GenericMetaStack implements MetaStack {
}
StringBuilder sb = new StringBuilder();
sb.append(startSpacer);
sb.append(definition.getStartSpacer());
for (int i = 0; i < ret.size(); i++) {
if (i != 0) {
sb.append(middleSpacer);
sb.append(definition.getMiddleSpacer());
}
MetaStackElement e = ret.get(i);
MetaStackEntry e = ret.get(i);
sb.append(e.getEntry().get().getValue());
}
sb.append(endSpacer);
sb.append(definition.getEndSpacer());
return sb.toString();
}
@Override
public MetaStack copy() {
return new GenericMetaStack(
elements.stream().map(MetaStackElement::copy).collect(ImmutableCollectors.toImmutableList()),
startSpacer,
middleSpacer,
endSpacer
);
public void accumulateToAll(LocalizedNode node) {
entries.forEach(e -> e.accumulateNode(node));
}
}

View File

@ -23,20 +23,19 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.caching.stacking;
package me.lucko.luckperms.common.metastacking;
import me.lucko.luckperms.api.LocalizedNode;
import java.util.List;
import me.lucko.luckperms.common.metastacking.definition.MetaStackDefinition;
public interface MetaStack {
List<MetaStackElement> getElements();
MetaStackDefinition getDefinition();
MetaType getTargetType();
String toFormattedString();
MetaStack copy();
default void accumulateToAll(LocalizedNode node) {
getElements().forEach(m -> m.accumulateNode(node));
}
void accumulateToAll(LocalizedNode node);
}

View File

@ -23,26 +23,22 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.caching.stacking;
package me.lucko.luckperms.common.metastacking;
import java.util.Collections;
import java.util.List;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.metastacking.definition.MetaStackElement;
public class NoopMetaStack implements MetaStack {
public static final NoopMetaStack INSTANCE = new NoopMetaStack();
import java.util.Map;
import java.util.Optional;
public interface MetaStackEntry {
MetaStack getParentStack();
MetaStackElement getElement();
Optional<Map.Entry<Integer, String>> getEntry();
boolean accumulateNode(LocalizedNode node);
@Override
public List<MetaStackElement> getElements() {
return Collections.emptyList();
}
@Override
public String toFormattedString() {
return null;
}
@Override
public MetaStack copy() {
return this;
}
}

View File

@ -23,51 +23,52 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.caching.stacking.elements;
package me.lucko.luckperms.common.metastacking;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.caching.stacking.MetaStackElement;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.api.Node;
import java.util.Map;
import java.util.Optional;
@RequiredArgsConstructor
public class LowestPriorityTrackElement implements MetaStackElement {
private final boolean prefix;
private final LuckPermsPlugin plugin;
private final String trackName;
private Map.Entry<Integer, String> entry = null;
public enum MetaType {
PREFIX {
@Override
public Optional<Map.Entry<Integer, String>> getEntry() {
return Optional.ofNullable(entry);
public boolean matches(Node node) {
return node.isPrefix();
}
@Override
public boolean accumulateNode(LocalizedNode node) {
if (MetaStackElement.checkMetaType(prefix, node)) {
return false;
}
Map.Entry<Integer, String> entry = prefix ? node.getPrefix() : node.getSuffix();
if (LowestPriorityElement.compareEntries(this.entry, entry)) {
return false;
}
if (MetaStackElement.checkTrackElement(plugin, node, trackName)) {
return false;
}
this.entry = entry;
return true;
public boolean shouldIgnore(Node node) {
return !node.isPrefix();
}
@Override
public MetaStackElement copy() {
return new LowestPriorityTrackElement(prefix, plugin, trackName);
public Map.Entry<Integer, String> getEntry(Node node) {
return node.getPrefix();
}
},
SUFFIX {
@Override
public boolean matches(Node node) {
return node.isSuffix();
}
@Override
public boolean shouldIgnore(Node node) {
return !node.isSuffix();
}
@Override
public Map.Entry<Integer, String> getEntry(Node node) {
return node.getSuffix();
}
};
public abstract boolean matches(Node node);
public abstract boolean shouldIgnore(Node node);
public abstract Map.Entry<Integer, String> getEntry(Node node);
}

View File

@ -23,47 +23,40 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.caching.stacking.elements;
package me.lucko.luckperms.common.metastacking;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.caching.stacking.MetaStackElement;
import me.lucko.luckperms.common.metastacking.definition.MetaStackElement;
import java.util.Map;
import java.util.Optional;
@Getter
@RequiredArgsConstructor
public class LowestPriorityOwnElement implements MetaStackElement {
private final boolean prefix;
private Map.Entry<Integer, String> entry = null;
public class SimpleMetaStackEntry implements MetaStackEntry {
private final MetaStack parentStack;
private final MetaStackElement element;
private final MetaType type;
@Getter(AccessLevel.NONE)
private Map.Entry<Integer, String> current = null;
@Override
public Optional<Map.Entry<Integer, String>> getEntry() {
return Optional.ofNullable(entry);
return Optional.ofNullable(current);
}
@Override
public boolean accumulateNode(LocalizedNode node) {
if (MetaStackElement.checkMetaType(prefix, node)) {
return false;
}
if (MetaStackElement.checkOwnElement(node)) {
return false;
}
Map.Entry<Integer, String> entry = prefix ? node.getPrefix() : node.getSuffix();
if (LowestPriorityElement.compareEntries(this.entry, entry)) {
return false;
}
this.entry = entry;
if (element.shouldAccumulate(node, type, current)) {
this.current = type.getEntry(node);
return true;
}
@Override
public MetaStackElement copy() {
return new LowestPriorityOwnElement(prefix);
return false;
}
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.metastacking.definition;
import me.lucko.luckperms.common.metastacking.GenericMetaStack;
import me.lucko.luckperms.common.metastacking.MetaStack;
import me.lucko.luckperms.common.metastacking.MetaType;
import java.util.List;
public interface MetaStackDefinition {
static MetaStackDefinition create(List<MetaStackElement> elements, String startSpacer, String middleSpacer, String endSpacer) {
return new SimpleMetaStackDefinition(elements, startSpacer, middleSpacer, endSpacer);
}
List<MetaStackElement> getElements();
String getStartSpacer();
String getMiddleSpacer();
String getEndSpacer();
default MetaStack newStack(MetaType type) {
return new GenericMetaStack(this, type);
}
}

View File

@ -23,48 +23,15 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.caching.stacking.elements;
import lombok.RequiredArgsConstructor;
package me.lucko.luckperms.common.metastacking.definition;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.caching.stacking.MetaStackElement;
import me.lucko.luckperms.common.metastacking.MetaType;
import java.util.Map;
import java.util.Optional;
@RequiredArgsConstructor
public class HighestPriorityOwnElement implements MetaStackElement {
private final boolean prefix;
private Map.Entry<Integer, String> entry = null;
public interface MetaStackElement {
@Override
public Optional<Map.Entry<Integer, String>> getEntry() {
return Optional.ofNullable(entry);
}
@Override
public boolean accumulateNode(LocalizedNode node) {
if (MetaStackElement.checkMetaType(prefix, node)) {
return false;
}
if (MetaStackElement.checkOwnElement(node)) {
return false;
}
Map.Entry<Integer, String> entry = prefix ? node.getPrefix() : node.getSuffix();
if (HighestPriorityElement.compareEntries(this.entry, entry)) {
return false;
}
this.entry = entry;
return true;
}
@Override
public MetaStackElement copy() {
return new HighestPriorityOwnElement(prefix);
}
boolean shouldAccumulate(LocalizedNode node, MetaType type, Map.Entry<Integer, String> current);
}

View File

@ -0,0 +1,53 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.metastacking.definition;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import com.google.common.collect.ImmutableList;
import java.util.List;
@Getter
@EqualsAndHashCode
@ToString
final class SimpleMetaStackDefinition implements MetaStackDefinition {
private final List<MetaStackElement> elements;
private final String startSpacer;
private final String middleSpacer;
private final String endSpacer;
public SimpleMetaStackDefinition(@NonNull List<MetaStackElement> elements, @NonNull String startSpacer, @NonNull String middleSpacer, @NonNull String endSpacer) {
this.elements = ImmutableList.copyOf(elements);
this.startSpacer = startSpacer;
this.middleSpacer = middleSpacer;
this.endSpacer = endSpacer;
}
}

View File

@ -0,0 +1,298 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.metastacking.definition;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.experimental.UtilityClass;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.common.core.model.Track;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@UtilityClass
public class StandardStackElements {
public static Optional<MetaStackElement> parseFromString(LuckPermsPlugin plugin, String s) {
s = s.toLowerCase();
if (s.equals("highest")) {
return Optional.of(new HighestPriority());
}
if (s.equals("lowest")) {
return Optional.of(new LowestPriority());
}
if (s.equals("highest_own")) {
return Optional.of(new HighestPriorityOwn());
}
if (s.equals("lowest_own")) {
return Optional.of(new LowestPriorityOwn());
}
if (s.startsWith("highest_on_track_") && s.length() > "highest_on_track_".length()) {
String track = s.substring("highest_on_track_".length());
return Optional.of(new HighestPriorityTrack(plugin, track));
}
if (s.startsWith("lowest_on_track_") && s.length() > "lowest_on_track_".length()) {
String track = s.substring("lowest_on_track_".length());
return Optional.of(new LowestPriorityTrack(plugin, track));
}
if (s.startsWith("highest_not_on_track_") && s.length() > "highest_not_on_track_".length()) {
String track = s.substring("highest_not_on_track_".length());
return Optional.of(new HighestPriorityNotOnTrack(plugin, track));
}
if (s.startsWith("lowest_not_on_track_") && s.length() > "lowest_not_on_track_".length()) {
String track = s.substring("lowest_not_on_track_".length());
return Optional.of(new LowestPriorityNotOnTrack(plugin, track));
}
new IllegalArgumentException("Cannot parse MetaStackElement: " + s).printStackTrace();
return Optional.empty();
}
public static List<MetaStackElement> parseList(LuckPermsPlugin plugin, List<String> strings) {
return strings.stream()
.map(s -> parseFromString(plugin, s))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(ImmutableCollectors.toImmutableList());
}
/**
* Returns true if the current node has the greater priority
* @param current the current entry
* @param newEntry the new entry
* @return true if the accumulation should return
*/
private static boolean compareEntriesHighest(Map.Entry<Integer, String> current, Map.Entry<Integer, String> newEntry) {
return current != null && current.getKey() >= newEntry.getKey();
}
/**
* Returns true if the current node has the lesser priority
* @param current the current entry
* @param newEntry the new entry
* @return true if the accumulation should return
*/
private static boolean compareEntriesLowest(Map.Entry<Integer, String> current, Map.Entry<Integer, String> newEntry) {
return current != null && current.getKey() <= newEntry.getKey();
}
/**
* Returns true if the node is not held by a user
* @param node the node to check
* @return true if the accumulation should return
*/
private static boolean checkOwnElement(LocalizedNode node) {
if (node.getLocation() == null || node.getLocation().equals("")) {
return true;
}
try {
UUID.fromString(node.getLocation());
return false;
} catch (IllegalArgumentException e) {
return true;
}
}
/**
* Returns true if the node is not held by a group on the track
* @param node the node to check
* @param track the track
* @return true if the accumulation should return
*/
private static boolean checkTrackElement(LuckPermsPlugin plugin, LocalizedNode node, String track) {
if (node.getLocation() == null || node.getLocation().equals("")) {
return true;
}
Track t = plugin.getTrackManager().getIfLoaded(track);
return t == null || !t.containsGroup(node.getLocation());
}
/**
* Returns true if the node is held by a group on the track
* @param node the node to check
* @param track the track
* @return true if the accumulation should return
*/
private static boolean checkNotTrackElement(LuckPermsPlugin plugin, LocalizedNode node, String track) {
// it's not come from a group on this track (from the user directly)
if (node.getLocation() == null || node.getLocation().equals("")) {
return false;
}
Track t = plugin.getTrackManager().getIfLoaded(track);
return t == null || t.containsGroup(node.getLocation());
}
@ToString
private static final class HighestPriority implements MetaStackElement {
@Override
public boolean shouldAccumulate(LocalizedNode node, MetaType type, Map.Entry<Integer, String> current) {
if (type.shouldIgnore(node)) {
return false;
}
Map.Entry<Integer, String> newEntry = type.getEntry(node);
return !compareEntriesHighest(current, newEntry);
}
}
@ToString
private static final class HighestPriorityOwn implements MetaStackElement {
@Override
public boolean shouldAccumulate(LocalizedNode node, MetaType type, Map.Entry<Integer, String> current) {
if (type.shouldIgnore(node)) {
return false;
}
if (checkOwnElement(node)) {
return false;
}
Map.Entry<Integer, String> newEntry = type.getEntry(node);
return !compareEntriesHighest(current, newEntry);
}
}
@ToString
@RequiredArgsConstructor
@EqualsAndHashCode(of = "trackName")
private static final class HighestPriorityTrack implements MetaStackElement {
private final LuckPermsPlugin plugin;
private final String trackName;
@Override
public boolean shouldAccumulate(LocalizedNode node, MetaType type, Map.Entry<Integer, String> current) {
if (type.shouldIgnore(node)) {
return false;
}
Map.Entry<Integer, String> newEntry = type.getEntry(node);
return !compareEntriesHighest(current, newEntry) && !checkTrackElement(plugin, node, trackName);
}
}
@ToString
@RequiredArgsConstructor
@EqualsAndHashCode(of = "trackName")
private static final class HighestPriorityNotOnTrack implements MetaStackElement {
private final LuckPermsPlugin plugin;
private final String trackName;
@Override
public boolean shouldAccumulate(LocalizedNode node, MetaType type, Map.Entry<Integer, String> current) {
if (type.shouldIgnore(node)) {
return false;
}
Map.Entry<Integer, String> newEntry = type.getEntry(node);
return !compareEntriesHighest(current, newEntry) && !checkNotTrackElement(plugin, node, trackName);
}
}
@ToString
private static final class LowestPriority implements MetaStackElement {
@Override
public boolean shouldAccumulate(LocalizedNode node, MetaType type, Map.Entry<Integer, String> current) {
if (type.shouldIgnore(node)) {
return false;
}
Map.Entry<Integer, String> newEntry = type.getEntry(node);
return !compareEntriesLowest(current, newEntry);
}
}
@ToString
private static final class LowestPriorityOwn implements MetaStackElement {
@Override
public boolean shouldAccumulate(LocalizedNode node, MetaType type, Map.Entry<Integer, String> current) {
if (type.shouldIgnore(node)) {
return false;
}
if (checkOwnElement(node)) {
return false;
}
Map.Entry<Integer, String> newEntry = type.getEntry(node);
return !compareEntriesLowest(current, newEntry);
}
}
@ToString
@RequiredArgsConstructor
@EqualsAndHashCode(of = "trackName")
private static final class LowestPriorityTrack implements MetaStackElement {
private final LuckPermsPlugin plugin;
private final String trackName;
@Override
public boolean shouldAccumulate(LocalizedNode node, MetaType type, Map.Entry<Integer, String> current) {
if (type.shouldIgnore(node)) {
return false;
}
Map.Entry<Integer, String> entry = type.getEntry(node);
return !compareEntriesLowest(current, entry) && !checkTrackElement(plugin, node, trackName);
}
}
@ToString
@RequiredArgsConstructor
@EqualsAndHashCode(of = "trackName")
private static final class LowestPriorityNotOnTrack implements MetaStackElement {
private final LuckPermsPlugin plugin;
private final String trackName;
@Override
public boolean shouldAccumulate(LocalizedNode node, MetaType type, Map.Entry<Integer, String> current) {
if (type.shouldIgnore(node)) {
return false;
}
Map.Entry<Integer, String> newEntry = type.getEntry(node);
return !compareEntriesLowest(current, newEntry) && !checkNotTrackElement(plugin, node, trackName);
}
}
}

View File

@ -58,9 +58,9 @@ public class SpongeSenderFactory extends SenderFactory<CommandSource> {
return Constants.CONSOLE_UUID;
}
@SuppressWarnings("deprecation")
@Override
protected void sendMessage(CommandSource source, String s) {
//noinspection deprecation
source.sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(s));
}

View File

@ -43,7 +43,18 @@ public class SpongeCalculatorLink implements ContextCalculator<Subject> {
@Override
public MutableContextSet giveApplicableContext(Subject subject, MutableContextSet accumulator) {
Set<Context> contexts = new HashSet<>();
Set<Context> contexts = new HashSet<Context>() {
// don't allow null elements
@Override
public boolean add(Context context) {
if (context == null) {
throw new NullPointerException("context");
}
return super.add(context);
}
};
try {
delegate.accumulateContexts(subject, contexts);

View File

@ -37,6 +37,7 @@ import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.caching.MetaAccumulator;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService;
@ -197,10 +198,10 @@ public class SpongeGroup extends Group {
try (Timing ignored = plugin.getService().getPlugin().getTimings().time(LPTiming.GROUP_GET_OPTION)) {
Optional<String> option;
if (s.equalsIgnoreCase("prefix")) {
option = getChatMeta(contexts, true);
option = getChatMeta(contexts, MetaType.PREFIX);
} else if (s.equalsIgnoreCase("suffix")) {
option = getChatMeta(contexts, false);
option = getChatMeta(contexts, MetaType.SUFFIX);
} else {
option = getMeta(contexts, s);
@ -226,13 +227,9 @@ public class SpongeGroup extends Group {
}
}
private Optional<String> getChatMeta(ImmutableContextSet contexts, boolean prefix) {
private Optional<String> getChatMeta(ImmutableContextSet contexts, MetaType type) {
MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, ExtractedContexts.generate(plugin.getService().calculateContexts(contexts)));
if (prefix) {
return Optional.ofNullable(metaAccumulator.getPrefixStack().toFormattedString());
} else {
return Optional.ofNullable(metaAccumulator.getSuffixStack().toFormattedString());
}
return Optional.ofNullable(metaAccumulator.getStack(type).toFormattedString());
}
private Optional<String> getMeta(ImmutableContextSet contexts, String key) {

View File

@ -41,6 +41,7 @@ import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.metastacking.MetaType;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
@ -369,26 +370,24 @@ public class LuckPermsSubjectData implements LPSubjectData {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_OPTION)) {
if (key.equalsIgnoreCase("prefix") || key.equalsIgnoreCase("suffix")) {
// special handling.
String type = key.toLowerCase();
boolean prefix = type.equals("prefix");
MetaType type = MetaType.valueOf(key.toUpperCase());
// remove all prefixes/suffixes from the user
List<Node> toRemove = streamNodes(enduring)
.filter(n -> prefix ? n.isPrefix() : n.isSuffix())
.filter(type::matches)
.filter(n -> n.getFullContexts().equals(context))
.collect(Collectors.toList());
toRemove.forEach(makeUnsetConsumer(enduring));
MetaAccumulator metaAccumulator = holder.accumulateMeta(null, null, ExtractedContexts.generate(service.calculateContexts(context)));
int priority = (type.equals("prefix") ? metaAccumulator.getPrefixes() : metaAccumulator.getSuffixes()).keySet().stream()
.mapToInt(e -> e).max().orElse(0);
int priority = metaAccumulator.getChatMeta(type).keySet().stream().mapToInt(e -> e).max().orElse(0);
priority += 10;
if (enduring) {
holder.setPermission(NodeFactory.makeChatMetaNode(type.equals("prefix"), priority, value).withExtraContext(context).build());
holder.setPermission(NodeFactory.makeChatMetaNode(type, priority, value).withExtraContext(context).build());
} else {
holder.setTransientPermission(NodeFactory.makeChatMetaNode(type.equals("prefix"), priority, value).withExtraContext(context).build());
holder.setTransientPermission(NodeFactory.makeChatMetaNode(type, priority, value).withExtraContext(context).build());
}
} else {