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