diff --git a/api/src/main/java/me/lucko/luckperms/api/caching/MetaData.java b/api/src/main/java/me/lucko/luckperms/api/caching/MetaData.java index abbebd47d..d31469573 100644 --- a/api/src/main/java/me/lucko/luckperms/api/caching/MetaData.java +++ b/api/src/main/java/me/lucko/luckperms/api/caching/MetaData.java @@ -25,6 +25,8 @@ package me.lucko.luckperms.api.caching; +import com.google.common.collect.ListMultimap; + import me.lucko.luckperms.api.metastacking.MetaStackDefinition; import java.util.Map; @@ -38,7 +40,24 @@ import java.util.SortedMap; public interface MetaData { /** - * Gets an immutable copy of the meta this user has + * Gets an immutable copy of the meta this user has. + * + *

A list multimap is used because when inherited values are included, each key can be + * mapped to multiple values.

+ * + *

The first value to be accumulated (and used to represent the key in {@link #getMeta()} is at index 0 + * in the list. Any additional values are stored in order of accumulation.

+ * + * @return an immutable multimap of meta + * @since 3.3 + */ + ListMultimap getMetaMultimap(); + + /** + * Gets an immutable copy of the meta this user has. + * + *

This map is formed by taking the entries in {@link #getMetaMultimap()}, and mapping each key + * to the value at index 0 in the corresponding list.

* * @return an immutable map of meta */ diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/MetaAccumulator.java b/common/src/main/java/me/lucko/luckperms/common/caching/MetaAccumulator.java index d6389bc44..a814ccbf3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/caching/MetaAccumulator.java +++ b/common/src/main/java/me/lucko/luckperms/common/caching/MetaAccumulator.java @@ -30,6 +30,9 @@ import lombok.Getter; import lombok.NonNull; import lombok.ToString; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; + import me.lucko.luckperms.api.ChatMetaType; import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.common.config.ConfigKeys; @@ -38,7 +41,6 @@ import me.lucko.luckperms.common.metastacking.MetaStack; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import java.util.Comparator; -import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; @@ -57,7 +59,7 @@ public class MetaAccumulator { } @Getter(AccessLevel.NONE) - private final Map meta; + private final ListMultimap meta; private final SortedMap prefixes; private final SortedMap suffixes; private int weight = 0; @@ -66,7 +68,7 @@ public class MetaAccumulator { private final MetaStack suffixStack; public MetaAccumulator(@NonNull MetaStack prefixStack, @NonNull MetaStack suffixStack) { - this.meta = new HashMap<>(); + this.meta = ArrayListMultimap.create(); this.prefixes = new TreeMap<>(Comparator.reverseOrder()); this.suffixes = new TreeMap<>(Comparator.reverseOrder()); this.prefixStack = prefixStack; @@ -76,9 +78,7 @@ public class MetaAccumulator { public void accumulateNode(LocalizedNode n) { if (n.isMeta()) { Map.Entry entry = n.getMeta(); - if (!meta.containsKey(entry.getKey())) { - meta.put(entry.getKey(), entry.getValue()); - } + meta.put(entry.getKey(), entry.getValue()); } if (n.isPrefix()) { @@ -106,7 +106,7 @@ public class MetaAccumulator { // We can assume that if this method is being called, this holder is effectively finalized. (it's not going to accumulate more nodes) // Therefore, it should be ok to set the weight meta key, if not already present. - public Map getMeta() { + public ListMultimap getMeta() { if (!this.meta.containsKey("weight") && this.weight != 0) { this.meta.put("weight", String.valueOf(this.weight)); } diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/MetaCache.java b/common/src/main/java/me/lucko/luckperms/common/caching/MetaCache.java index dd6e4eac7..7791ee831 100644 --- a/common/src/main/java/me/lucko/luckperms/common/caching/MetaCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/caching/MetaCache.java @@ -25,16 +25,20 @@ package me.lucko.luckperms.common.caching; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.ListMultimap; import me.lucko.luckperms.api.caching.MetaData; import me.lucko.luckperms.api.metastacking.MetaStackDefinition; import me.lucko.luckperms.common.metastacking.MetaStack; +import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.concurrent.locks.ReadWriteLock; @@ -43,29 +47,38 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Holds a user's cached meta for a given context */ +@Getter @NoArgsConstructor public class MetaCache implements MetaData { + @Getter(AccessLevel.NONE) private final ReadWriteLock lock = new ReentrantReadWriteLock(); - @Getter + private ListMultimap metaMultimap = ImmutableListMultimap.of(); private Map meta = ImmutableMap.of(); - - @Getter private SortedMap prefixes = ImmutableSortedMap.of(); - - @Getter private SortedMap suffixes = ImmutableSortedMap.of(); - - @Getter private MetaStack prefixStack = null; - - @Getter private MetaStack suffixStack = null; public void loadMeta(MetaAccumulator meta) { lock.writeLock().lock(); try { - this.meta = ImmutableMap.copyOf(meta.getMeta()); + this.metaMultimap = ImmutableListMultimap.copyOf(meta.getMeta()); + + //noinspection unchecked + Map> metaMap = (Map) this.metaMultimap.asMap(); + ImmutableMap.Builder metaMapBuilder = ImmutableMap.builder(); + + for (Map.Entry> e : metaMap.entrySet()) { + if (e.getValue().isEmpty()) { + continue; + } + + // take the value which was accumulated first + metaMapBuilder.put(e.getKey(), e.getValue().get(0)); + } + this.meta = metaMapBuilder.build(); + this.prefixes = ImmutableSortedMap.copyOfSorted(meta.getPrefixes()); this.suffixes = ImmutableSortedMap.copyOfSorted(meta.getSuffixes()); this.prefixStack = meta.getPrefixStack(); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java index 5b2087467..96a249099 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java @@ -30,6 +30,7 @@ import lombok.Getter; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ListMultimap; import me.lucko.luckperms.api.ChatMetaType; import me.lucko.luckperms.api.LocalizedNode; @@ -54,6 +55,7 @@ import org.spongepowered.api.service.permission.PermissionService; import co.aikar.timings.Timing; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -238,8 +240,9 @@ public class SpongeGroup extends Group { private Optional getMeta(ImmutableContextSet contexts, String key) { MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, ExtractedContexts.generate(plugin.getService().calculateContexts(contexts))); - Map meta = metaAccumulator.getMeta(); - return Optional.ofNullable(meta.get(key)); + ListMultimap meta = metaAccumulator.getMeta(); + List ret = meta.get(key); + return ret.isEmpty() ? Optional.empty() : Optional.of(ret.get(0)); } } }