diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsSubscriptionMap.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsSubscriptionMap.java index e6d0dc532..01ee9948a 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsSubscriptionMap.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsSubscriptionMap.java @@ -25,10 +25,7 @@ package me.lucko.luckperms.bukkit.inject.server; -import com.google.common.collect.Maps; - import me.lucko.luckperms.bukkit.LPBukkitPlugin; -import me.lucko.luckperms.common.util.ImmutableCollectors; import org.bukkit.entity.Player; import org.bukkit.permissions.Permissible; @@ -40,10 +37,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; -import java.util.function.Function; /** * A replacement map for the 'permSubs' instance in Bukkit's SimplePluginManager. @@ -67,15 +62,17 @@ import java.util.function.Function; * * Injected by {@link InjectorSubscriptionMap}. */ -public final class LuckPermsSubscriptionMap extends HashMap> { +public final class LuckPermsSubscriptionMap implements Map> { // the plugin instance final LPBukkitPlugin plugin; + private final Map> subscriptions = Collections.synchronizedMap(new WeakHashMap<>()); + public LuckPermsSubscriptionMap(LPBukkitPlugin plugin, Map> existingData) { this.plugin = plugin; for (Entry> entry : existingData.entrySet()) { - super.put(entry.getKey(), new LPSubscriptionValueMap(entry.getKey(), entry.getValue())); + entry.getValue().keySet().forEach(permissible -> subscribe(permissible, entry.getKey())); } } @@ -84,61 +81,55 @@ public final class LuckPermsSubscriptionMap extends HashMap get(Object key) { - if (key == null || !(key instanceof String)) { - return null; + return new ValueMap((String) key); + } + + public void subscribe(Permissible permissible, String permission) { + // don't allow players to be put into this map + if (permissible instanceof Player) { + return; } - String permission = (String) key; + Set perms = this.subscriptions.computeIfAbsent(permissible, x -> Collections.synchronizedSet(new HashSet<>())); + perms.add(permission); + } - LPSubscriptionValueMap result = (LPSubscriptionValueMap) super.get(key); - if (result == null) { - // calculate a new map - always! - result = new LPSubscriptionValueMap(permission); - super.put(permission, result); + public boolean unsubscribe(Permissible permissible, String permission) { + if (permissible instanceof Player) { + return false; // ignore calls for players } - return result; - } + Set perms = this.subscriptions.get(permissible); - @Override - public Map put(String key, Map value) { - if (value == null) { - throw new NullPointerException("Map value cannot be null"); + if (perms == null) { + return false; } - // ensure values are LP subscription maps - if (!(value instanceof LPSubscriptionValueMap)) { - value = new LPSubscriptionValueMap(key, value); + return perms.remove(permission); + } + + public @NonNull Set subscribers(String permission) { + Collection onlinePlayers = this.plugin.getBootstrap().getServer().getOnlinePlayers(); + Set set = new HashSet<>(onlinePlayers.size() + this.subscriptions.size()); + + // add permissibles from the subscriptions map + this.subscriptions.forEach((permissible, perms) -> { + if (perms.contains(permission)) { + set.add(permissible); + } + }); + + // add any online players who meet requirements + for (Player player : onlinePlayers) { + if (player.hasPermission(permission) || player.isPermissionSet(permission)) { + set.add(player); + } } - return super.put(key, value); - } - @Override - public void putAll(Map> m) { - m.forEach(this::put); - } - - @Override - public Map putIfAbsent(String key, Map value) { - return get(key); - } - - @Override - public Map computeIfAbsent(String key, Function> mappingFunction) { - return get(key); - } - - // if the key isn't null and is a string, #get will always return a value for it - @Override - public boolean containsKey(Object key) { - return key != null && key instanceof String; + return set; } /** @@ -148,109 +139,53 @@ public final class LuckPermsSubscriptionMap extends HashMap> detach() { Map> map = new HashMap<>(); - for (Map.Entry> ent : entrySet()) { - map.put(ent.getKey(), new WeakHashMap<>(((LPSubscriptionValueMap) ent.getValue()).backing)); - } + this.subscriptions.forEach((permissible, perms) -> { + for (String perm : perms) { + map.computeIfAbsent(perm, x -> new WeakHashMap<>()).put(permissible, true); + } + }); return map; } + @Override public Map put(String key, Map value) { throw new UnsupportedOperationException(); } + @Override public Map remove(Object key) { throw new UnsupportedOperationException(); } + @Override public void putAll(Map> m) { throw new UnsupportedOperationException(); } + @Override public void clear() { throw new UnsupportedOperationException(); } + @Override public Set keySet() { throw new UnsupportedOperationException(); } + @Override public Collection> values() { throw new UnsupportedOperationException(); } + @Override public Set>> entrySet() { throw new UnsupportedOperationException(); } + @Override public int size() { throw new UnsupportedOperationException(); } + @Override public boolean isEmpty() { throw new UnsupportedOperationException(); } + @Override public boolean containsKey(Object key) { throw new UnsupportedOperationException(); } + @Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); } + /** * Value map extension which includes LP objects in Permissible related queries. */ - public final class LPSubscriptionValueMap implements Map { + public final class ValueMap implements Map { // the permission being mapped to this value map private final String permission; - // the backing map - private final Map backing; - - private LPSubscriptionValueMap(String permission, Map backing) { + public ValueMap(String permission) { this.permission = permission; - this.backing = Collections.synchronizedMap(new WeakHashMap<>(backing)); - - // remove all players from the map - this.backing.keySet().removeIf(p -> p instanceof Player); - } - - public LPSubscriptionValueMap(String permission) { - this.permission = permission; - this.backing = Collections.synchronizedMap(new WeakHashMap<>()); - } - - @Override - public Boolean get(Object key) { - boolean isPlayer = key instanceof Player; - - // if the key is a player, check their LPPermissible first - if (isPlayer) { - Permissible p = (Permissible) key; - if (p.hasPermission(this.permission)) { - return true; - } - } - - // then try the map - Boolean result = this.backing.get(key); - if (result != null) { - return result; - } - - // then try the permissible, if we haven't already - if (!isPlayer && key instanceof Permissible) { - Permissible p = (Permissible) key; - if (p.hasPermission(this.permission)) { - return true; - } - } - - // no result - return null; } @Override public Boolean put(Permissible key, Boolean value) { - // don't allow players to be put into this map - if (key instanceof Player) { - return true; - } - - return this.backing.put(key, value); + subscribe(key, this.permission); + return null; } @Override - public boolean containsKey(Object key) { - // delegate through the get method - return get(key) != null; + public Boolean remove(Object k) { + Permissible key = (Permissible) k; + return unsubscribe(key, this.permission) ? true : null; } @Override public @NonNull Set keySet() { - // start with the backing set - Set set; - synchronized (this.backing) { - set = new HashSet<>(this.backing.keySet()); - } - - // add any online players who meet requirements - for (Player player : LuckPermsSubscriptionMap.this.plugin.getBootstrap().getServer().getOnlinePlayers()) { - if (player.hasPermission(this.permission) || player.isPermissionSet(this.permission)) { - set.add(player); - } - } - - return set; - } - - @Override - public @NonNull Set> entrySet() { - return keySet().stream() - .map(key -> { - Boolean value = get(key); - return value != null ? Maps.immutableEntry(key, value) : null; - }) - .filter(Objects::nonNull) - .collect(ImmutableCollectors.toSet()); + return subscribers(this.permission); } @Override @@ -262,34 +197,15 @@ public final class LuckPermsSubscriptionMap extends HashMap m) { - this.backing.putAll(m); - } - - @Override - public void clear() { - this.backing.clear(); - } - - @Override - public @NonNull Collection values() { - return this.backing.values(); - } + @Override public void putAll(Map m) { throw new UnsupportedOperationException(); } + @Override public void clear() { throw new UnsupportedOperationException(); } + @Override public Collection values() { throw new UnsupportedOperationException(); } + @Override public Set> entrySet() { throw new UnsupportedOperationException(); } + @Override public boolean containsKey(Object key) { throw new UnsupportedOperationException(); } + @Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); } + @Override public Boolean get(Object key) { throw new UnsupportedOperationException(); } } }