mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-24 11:38:40 +01:00
Inject our own subscription map into the plugin manager instead of registering permissions individually for each player
This commit is contained in:
parent
72e6c75433
commit
dee41b315f
@ -39,6 +39,7 @@ import me.lucko.luckperms.bukkit.listeners.BukkitPlatformListener;
|
||||
import me.lucko.luckperms.bukkit.messaging.BukkitMessagingFactory;
|
||||
import me.lucko.luckperms.bukkit.model.Injector;
|
||||
import me.lucko.luckperms.bukkit.model.LPPermissible;
|
||||
import me.lucko.luckperms.bukkit.model.SubscriptionMapInjector;
|
||||
import me.lucko.luckperms.bukkit.processors.BukkitProcessorsSetupTask;
|
||||
import me.lucko.luckperms.bukkit.processors.ChildPermissionProvider;
|
||||
import me.lucko.luckperms.bukkit.processors.DefaultsProvider;
|
||||
@ -244,6 +245,13 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
contextManager.registerCalculator(new WorldCalculator(this));
|
||||
contextManager.registerCalculator(new LuckPermsCalculator<>(getConfiguration()), true);
|
||||
|
||||
// inject our own subscription map
|
||||
new SubscriptionMapInjector(this).run();
|
||||
|
||||
// schedule another injection after all plugins have loaded - the entire pluginmanager instance
|
||||
// is replaced by some plugins :(
|
||||
scheduler.asyncLater(new SubscriptionMapInjector(this), 2L);
|
||||
|
||||
// Provide vault support
|
||||
tryVaultHook(false);
|
||||
|
||||
@ -324,9 +332,10 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
permissionVault.shutdown();
|
||||
verboseHandler.shutdown();
|
||||
|
||||
// uninject from players
|
||||
for (Player player : getServer().getOnlinePlayers()) {
|
||||
try {
|
||||
Injector.unInject(player, false, false);
|
||||
Injector.unInject(player, false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -342,6 +351,9 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
// uninject subscription map
|
||||
SubscriptionMapInjector.uninject();
|
||||
|
||||
getLog().info("Closing storage...");
|
||||
storage.shutdown();
|
||||
|
||||
@ -388,14 +400,6 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserRefresh(User user) {
|
||||
LPPermissible lpp = Injector.getPermissible(uuidCache.getExternalUUID(user.getUuid()));
|
||||
if (lpp != null) {
|
||||
lpp.updateSubscriptions();
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshAutoOp(User user, Player player) {
|
||||
if (user == null) {
|
||||
return;
|
||||
|
@ -209,7 +209,7 @@ public class BukkitConnectionListener implements Listener {
|
||||
|
||||
// Remove the custom permissible
|
||||
try {
|
||||
Injector.unInject(player, true, true);
|
||||
Injector.unInject(player, true);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
@ -35,9 +35,6 @@ import org.bukkit.permissions.PermissionAttachment;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Injects a {@link LPPermissible} into a {@link Player}.
|
||||
@ -47,7 +44,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
@UtilityClass
|
||||
public class Injector {
|
||||
private static final Map<UUID, LPPermissible> INJECTED_PERMISSIBLES = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* All permission checks made on standard Bukkit objects are effectively proxied to a
|
||||
@ -123,13 +119,9 @@ public class Injector {
|
||||
// Setup the new permissible
|
||||
newPermissible.getActive().set(true);
|
||||
newPermissible.setOldPermissible(oldPermissible);
|
||||
newPermissible.updateSubscriptionsAsync();
|
||||
|
||||
// inject the new instance
|
||||
HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, newPermissible);
|
||||
|
||||
// register the injection with the map
|
||||
INJECTED_PERMISSIBLES.put(player.getUniqueId(), newPermissible);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,10 +129,9 @@ public class Injector {
|
||||
*
|
||||
* @param player the player to uninject from
|
||||
* @param dummy if the replacement permissible should be a dummy.
|
||||
* @param unsubscribe if the extracted permissible should unsubscribe itself. see {@link SubscriptionManager}.
|
||||
* @throws Exception propagates any exceptions which were thrown during uninjection
|
||||
*/
|
||||
public static void unInject(Player player, boolean dummy, boolean unsubscribe) throws Exception {
|
||||
public static void unInject(Player player, boolean dummy) throws Exception {
|
||||
|
||||
// gets the players current permissible.
|
||||
PermissibleBase permissible = (PermissibleBase) HUMAN_ENTITY_PERMISSIBLE_FIELD.get(player);
|
||||
@ -152,11 +143,6 @@ public class Injector {
|
||||
// clear all permissions
|
||||
lpPermissible.clearPermissions();
|
||||
|
||||
// try to unsubscribe
|
||||
if (unsubscribe) {
|
||||
lpPermissible.unsubscribeFromAllAsync();
|
||||
}
|
||||
|
||||
// set to inactive
|
||||
lpPermissible.getActive().set(false);
|
||||
|
||||
@ -174,12 +160,6 @@ public class Injector {
|
||||
HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, newPb);
|
||||
}
|
||||
}
|
||||
|
||||
INJECTED_PERMISSIBLES.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public static LPPermissible getPermissible(UUID uuid) {
|
||||
return INJECTED_PERMISSIBLES.get(uuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import lombok.Setter;
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.common.caching.UserCache;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.verbose.CheckOrigin;
|
||||
@ -45,7 +44,6 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -78,9 +76,6 @@ public class LPPermissible extends PermissibleBase {
|
||||
// the luckperms plugin instance
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
// the subscription manager, handling the players permission subscriptions.
|
||||
private final SubscriptionManager subscriptions;
|
||||
|
||||
// the players previous permissible. (the one they had before this one was injected)
|
||||
@Setter
|
||||
private PermissibleBase oldPermissible = null;
|
||||
@ -97,7 +92,6 @@ public class LPPermissible extends PermissibleBase {
|
||||
this.user = user;
|
||||
this.player = player;
|
||||
this.plugin = plugin;
|
||||
this.subscriptions = new SubscriptionManager(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -140,56 +134,6 @@ public class LPPermissible extends PermissibleBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the players subscriptions asynchronously
|
||||
*/
|
||||
public void updateSubscriptionsAsync() {
|
||||
if (!active.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getScheduler().doAsync(this::updateSubscriptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the players subscriptions
|
||||
*/
|
||||
public void updateSubscriptions() {
|
||||
if (!active.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserCache cache = user.getUserData();
|
||||
|
||||
// calculate their "active" permissions
|
||||
Set<String> ent = new HashSet<>(cache.getPermissionData(calculateContexts()).getImmutableBacking().keySet());
|
||||
|
||||
// include defaults, if enabled.
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
if (player.isOp()) {
|
||||
ent.addAll(plugin.getDefaultsProvider().getOpDefaults().keySet());
|
||||
} else {
|
||||
ent.addAll(plugin.getDefaultsProvider().getNonOpDefaults().keySet());
|
||||
}
|
||||
}
|
||||
|
||||
subscriptions.subscribe(ent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from all permissions asynchronously
|
||||
*/
|
||||
public void unsubscribeFromAllAsync() {
|
||||
plugin.getScheduler().doAsync(this::unsubscribeFromAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from all permissions
|
||||
*/
|
||||
public void unsubscribeFromAll() {
|
||||
subscriptions.subscribe(Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds attachments to this permissible.
|
||||
*
|
||||
|
@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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.bukkit.model;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A replacement map for the 'permSubs' instance in Bukkit's SimplePluginManager.
|
||||
*
|
||||
* This instance allows LuckPerms to intercept calls to
|
||||
* {@link PluginManager#subscribeToPermission(String, Permissible)},
|
||||
* {@link PluginManager#unsubscribeFromPermission(String, Permissible)} and
|
||||
* {@link PluginManager#getPermissionSubscriptions(String)}.
|
||||
*
|
||||
* Bukkit for some reason sometimes uses subscription status to determine whether
|
||||
* a permissible has a given node, instead of checking directly with
|
||||
* {@link Permissible#hasPermission(Permission)}.
|
||||
*
|
||||
* {@link org.bukkit.Server#broadcast(String, String)} is a good example of this.
|
||||
*
|
||||
* In order to implement predicable Bukkit behaviour, LP has two options:
|
||||
* 1) register subscriptions for all players as normal, or
|
||||
* 2) inject it's own map instance to proxy calls to {@link PluginManager#getPermissionSubscriptions(String)} back to LuckPerms.
|
||||
*
|
||||
* This class implements option 2 above. It is preferred because it is faster & uses less memory
|
||||
*/
|
||||
public class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>> {
|
||||
|
||||
// the plugin instance
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
public LPSubscriptionMap(LPBukkitPlugin plugin, Map<String, Map<Permissible, Boolean>> existingData) {
|
||||
super(existingData);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/* The get method is the only one which is actually used by SimplePluginManager
|
||||
* we override it to always return a value - which means the null check in
|
||||
* subscribeToDefaultPerms always fails - soo, we don't have to worry too much
|
||||
* about implementing #put.
|
||||
*
|
||||
* we also ensure all returns are LPSubscriptionValueMaps. this extension
|
||||
* will also delegate checks to online players - meaning we don't ever
|
||||
* have to register their subscriptions with the plugin manager.
|
||||
*/
|
||||
@Override
|
||||
public Map<Permissible, Boolean> get(Object key) {
|
||||
if (key == null || !(key instanceof String)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String permission = ((String) key);
|
||||
|
||||
Map<Permissible, Boolean> result = super.get(key);
|
||||
|
||||
if (result == null) {
|
||||
// calculate a new map - always!
|
||||
result = new LPSubscriptionValueMap(permission);
|
||||
super.put(permission, result);
|
||||
} else if (!(result instanceof LPSubscriptionValueMap)) {
|
||||
// ensure return type is a LPSubscriptionMap
|
||||
result = new LPSubscriptionValueMap(permission, result);
|
||||
super.put(permission, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Permissible, Boolean> put(String key, Map<Permissible, Boolean> value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException("Map value cannot be null");
|
||||
}
|
||||
|
||||
// ensure values are LP subscription maps
|
||||
if (!(value instanceof LPSubscriptionValueMap)) {
|
||||
value = new LPSubscriptionValueMap(key, value);
|
||||
}
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this map back to a standard HashMap
|
||||
*
|
||||
* @return a standard representation of this map
|
||||
*/
|
||||
public Map<String, Map<Permissible, Boolean>> detach() {
|
||||
Map<String, Map<Permissible, Boolean>> ret = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Map<Permissible, Boolean>> ent : entrySet()) {
|
||||
if (ent.getValue() instanceof LPSubscriptionValueMap) {
|
||||
ret.put(ent.getKey(), ((LPSubscriptionValueMap) ent.getValue()).backing);
|
||||
} else {
|
||||
ret.put(ent.getKey(), ent.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value map extension which includes LP objects in Permissible related queries.
|
||||
*/
|
||||
public class LPSubscriptionValueMap implements Map<Permissible, Boolean> {
|
||||
|
||||
// the permission being mapped to this value map
|
||||
private final String permission;
|
||||
|
||||
// the backing map
|
||||
private final Map<Permissible, Boolean> backing;
|
||||
|
||||
public LPSubscriptionValueMap(String permission, Map<Permissible, Boolean> backing) {
|
||||
this.permission = permission;
|
||||
this.backing = backing;
|
||||
}
|
||||
|
||||
public LPSubscriptionValueMap(String permission) {
|
||||
this(permission, 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.isPermissionSet(permission)) {
|
||||
return p.hasPermission(permission);
|
||||
}
|
||||
}
|
||||
|
||||
// then try the map
|
||||
Boolean result = backing.get(key);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// then try the map, if we haven't already
|
||||
if (!isPlayer && key instanceof Permissible) {
|
||||
Permissible p = (Permissible) key;
|
||||
if (p.isPermissionSet(permission)) {
|
||||
return p.hasPermission(permission);
|
||||
}
|
||||
}
|
||||
|
||||
// no result
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
// check the backing map, as well as if the permissible has the perm set
|
||||
return backing.containsKey(key) || (key instanceof Permissible && ((Permissible) key).isPermissionSet(permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Permissible> keySet() {
|
||||
// gather players (LPPermissibles)
|
||||
Set<Permissible> players = plugin.getServer().getOnlinePlayers().stream()
|
||||
.filter(player -> player.isPermissionSet(permission))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// then combine the players with the backing map
|
||||
return Sets.union(players, backing.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<Permissible, Boolean>> entrySet() {
|
||||
return keySet().stream()
|
||||
.map(p -> {
|
||||
Boolean ret = get(p);
|
||||
return ret != null ? Maps.immutableEntry(p, ret) : null;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(ImmutableCollectors.toImmutableSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
// we never want to remove this map from the parent - since it just gets recreated
|
||||
// on subsequent calls
|
||||
return false;
|
||||
}
|
||||
|
||||
// just delegate to the backing map
|
||||
|
||||
@Override
|
||||
public Boolean put(Permissible key, Boolean value) {
|
||||
return backing.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean remove(Object key) {
|
||||
return backing.remove(key);
|
||||
}
|
||||
|
||||
// the following methods are not used in the current impls of PluginManager, but just delegate them for now
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return backing.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return backing.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends Permissible, ? extends Boolean> m) {
|
||||
backing.putAll(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
backing.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Boolean> values() {
|
||||
return backing.values();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,103 +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.bukkit.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.bukkit.permissions.Permission;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Handles permission subscriptions with Bukkits plugin manager, for a given LPPermissible.
|
||||
*
|
||||
* Bukkit for some reason sometimes uses subscription status to determine whether a permissible has a given node, instead
|
||||
* of checking directly with {@link org.bukkit.permissions.Permissible#hasPermission(Permission)}.
|
||||
*
|
||||
* {@link org.bukkit.Bukkit#broadcast(String, String)} is a good example of this.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class SubscriptionManager {
|
||||
|
||||
private final LPPermissible permissible;
|
||||
private Set<String> currentSubscriptions = ImmutableSet.of();
|
||||
|
||||
public synchronized void subscribe(Set<String> perms) {
|
||||
Set<String> newPerms = ImmutableSet.copyOf(perms);
|
||||
|
||||
// we compare changes to avoid unnecessary time wasted on the main thread mutating this data.
|
||||
// the changes can be calculated here async, and then only the needed changes can be applied.
|
||||
Map.Entry<Set<String>, Set<String>> changes = compareSets(newPerms, currentSubscriptions);
|
||||
if (!changes.getKey().isEmpty() || !changes.getValue().isEmpty()) {
|
||||
permissible.getPlugin().getScheduler().doSync(new SubscriptionUpdateTask(permissible, changes.getKey(), changes.getValue()));
|
||||
}
|
||||
|
||||
this.currentSubscriptions = newPerms;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public static final class SubscriptionUpdateTask implements Runnable {
|
||||
private final LPPermissible permissible;
|
||||
private final Set<String> toAdd;
|
||||
private final Set<String> toRemove;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (String s : toAdd) {
|
||||
permissible.getPlugin().getServer().getPluginManager().subscribeToPermission(s, permissible.getPlayer());
|
||||
}
|
||||
for (String s : toRemove) {
|
||||
permissible.getPlugin().getServer().getPluginManager().unsubscribeFromPermission(s, permissible.getPlayer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two sets
|
||||
* @param local the local set
|
||||
* @param remote the remote set
|
||||
* @return the entries to add to remote, and the entries to remove from remote
|
||||
*/
|
||||
private static Map.Entry<Set<String>, Set<String>> compareSets(Set<String> local, Set<String> remote) {
|
||||
// entries in local but not remote need to be added
|
||||
// entries in remote but not local need to be removed
|
||||
|
||||
Set<String> toAdd = new HashSet<>(local);
|
||||
toAdd.removeAll(remote);
|
||||
|
||||
Set<String> toRemove = new HashSet<>(remote);
|
||||
toRemove.removeAll(local);
|
||||
|
||||
return Maps.immutableEntry(toAdd, toRemove);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.bukkit.model;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.SimplePluginManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SubscriptionMapInjector implements Runnable {
|
||||
private static final Field PERM_SUBS_FIELD;
|
||||
|
||||
static {
|
||||
Field permSubsField;
|
||||
try {
|
||||
permSubsField = SimplePluginManager.class.getDeclaredField("permSubs");
|
||||
permSubsField.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
PERM_SUBS_FIELD = permSubsField;
|
||||
}
|
||||
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
inject();
|
||||
} catch (Exception e) {
|
||||
plugin.getLog().severe("Exception occurred whilst injecting LuckPerms Permission Subscription map.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void inject() throws Exception {
|
||||
PluginManager pluginManager = plugin.getServer().getPluginManager();
|
||||
|
||||
if (!(pluginManager instanceof SimplePluginManager)) {
|
||||
plugin.getLog().severe("PluginManager instance is not a 'SimplePluginManager', instead: " + pluginManager.getClass());
|
||||
plugin.getLog().severe("Unable to inject LuckPerms Permission Subscription map.");
|
||||
return;
|
||||
}
|
||||
|
||||
Object map = PERM_SUBS_FIELD.get(pluginManager);
|
||||
if (map instanceof LPSubscriptionMap) {
|
||||
return; // already injected
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
Map<String, Map<Permissible, Boolean>> castedMap = (Map<String, Map<Permissible, Boolean>>) map;
|
||||
|
||||
// make a new subscription map
|
||||
LPSubscriptionMap newMap = new LPSubscriptionMap(plugin, castedMap);
|
||||
|
||||
// inject it
|
||||
PERM_SUBS_FIELD.set(pluginManager, newMap);
|
||||
}
|
||||
|
||||
public static void uninject() {
|
||||
try {
|
||||
PluginManager pluginManager = Bukkit.getServer().getPluginManager();
|
||||
if (!(pluginManager instanceof SimplePluginManager)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object map = PERM_SUBS_FIELD.get(pluginManager);
|
||||
if (map instanceof LPSubscriptionMap) {
|
||||
LPSubscriptionMap lpMap = (LPSubscriptionMap) map;
|
||||
PERM_SUBS_FIELD.set(pluginManager, lpMap.detach());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -45,7 +45,7 @@ public class DefaultsProcessor implements PermissionProcessor {
|
||||
|
||||
@Override
|
||||
public Tristate hasPermission(String permission) {
|
||||
Tristate t = defaultsProvider.hasDefault(permission, isOp);
|
||||
Tristate t = defaultsProvider.lookup(permission, isOp);
|
||||
if (t != Tristate.UNDEFINED) {
|
||||
return t;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ public class DefaultsProvider {
|
||||
* @param isOp if the player is op
|
||||
* @return a tristate result
|
||||
*/
|
||||
public Tristate hasDefault(String permission, boolean isOp) {
|
||||
public Tristate lookup(String permission, boolean isOp) {
|
||||
Map<String, Boolean> map = isOp ? opDefaults : nonOpDefaults;
|
||||
|
||||
Boolean b = map.get(permission);
|
||||
@ -126,6 +126,10 @@ public class DefaultsProvider {
|
||||
unregisterDefaults(nonOpDefaults, nonOpDummy, false);
|
||||
}
|
||||
|
||||
private static PluginManager pm() {
|
||||
return Bukkit.getServer().getPluginManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters defaults for a given permissible.
|
||||
*
|
||||
@ -136,21 +140,21 @@ public class DefaultsProvider {
|
||||
Set<String> perms = map.keySet();
|
||||
|
||||
for (String name : perms) {
|
||||
Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, p);
|
||||
pm().unsubscribeFromPermission(name, p);
|
||||
}
|
||||
|
||||
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(op, p);
|
||||
pm().unsubscribeFromDefaultPerms(op, p);
|
||||
}
|
||||
|
||||
private static void calculateDefaults(Map<String, Boolean> map, DummyPermissible p, boolean op) {
|
||||
Bukkit.getServer().getPluginManager().subscribeToDefaultPerms(op, p);
|
||||
pm().subscribeToDefaultPerms(op, p);
|
||||
|
||||
Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(op);
|
||||
Set<Permission> defaults = pm().getDefaultPermissions(op);
|
||||
for (Permission perm : defaults) {
|
||||
String name = perm.getName().toLowerCase();
|
||||
|
||||
map.put(name, true);
|
||||
Bukkit.getServer().getPluginManager().subscribeToPermission(name, p);
|
||||
pm().subscribeToPermission(name, p);
|
||||
|
||||
// register defaults for any children too
|
||||
calculateChildPermissions(map, p, perm.getChildren(), false);
|
||||
@ -167,10 +171,10 @@ public class DefaultsProvider {
|
||||
boolean value = e.getValue() ^ invert;
|
||||
|
||||
accumulator.put(e.getKey().toLowerCase(), value);
|
||||
Bukkit.getServer().getPluginManager().subscribeToPermission(e.getKey(), p);
|
||||
pm().subscribeToPermission(e.getKey(), p);
|
||||
|
||||
// lookup any deeper children & resolve if present
|
||||
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
|
||||
Permission perm = pm().getPermission(e.getKey());
|
||||
if (perm != null) {
|
||||
calculateChildPermissions(accumulator, p, perm.getChildren(), !value);
|
||||
}
|
||||
|
@ -381,15 +381,6 @@ public interface LuckPermsPlugin {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a users data is refreshed
|
||||
*
|
||||
* @param user the user
|
||||
*/
|
||||
default void onUserRefresh(User user) {
|
||||
|
||||
}
|
||||
|
||||
static void sendStartupBanner(Sender sender, LuckPermsPlugin plugin) {
|
||||
sender.sendMessage(Util.color("&b __ &3 __ ___ __ __ "));
|
||||
sender.sendMessage(Util.color("&b | | | / ` |__/ &3|__) |__ |__) |\\/| /__` "));
|
||||
|
Loading…
Reference in New Issue
Block a user