mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-27 21:29:47 +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.messaging.BukkitMessagingFactory;
|
||||||
import me.lucko.luckperms.bukkit.model.Injector;
|
import me.lucko.luckperms.bukkit.model.Injector;
|
||||||
import me.lucko.luckperms.bukkit.model.LPPermissible;
|
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.BukkitProcessorsSetupTask;
|
||||||
import me.lucko.luckperms.bukkit.processors.ChildPermissionProvider;
|
import me.lucko.luckperms.bukkit.processors.ChildPermissionProvider;
|
||||||
import me.lucko.luckperms.bukkit.processors.DefaultsProvider;
|
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 WorldCalculator(this));
|
||||||
contextManager.registerCalculator(new LuckPermsCalculator<>(getConfiguration()), true);
|
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
|
// Provide vault support
|
||||||
tryVaultHook(false);
|
tryVaultHook(false);
|
||||||
|
|
||||||
@ -324,9 +332,10 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
|||||||
permissionVault.shutdown();
|
permissionVault.shutdown();
|
||||||
verboseHandler.shutdown();
|
verboseHandler.shutdown();
|
||||||
|
|
||||||
|
// uninject from players
|
||||||
for (Player player : getServer().getOnlinePlayers()) {
|
for (Player player : getServer().getOnlinePlayers()) {
|
||||||
try {
|
try {
|
||||||
Injector.unInject(player, false, false);
|
Injector.unInject(player, false);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -342,6 +351,9 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uninject subscription map
|
||||||
|
SubscriptionMapInjector.uninject();
|
||||||
|
|
||||||
getLog().info("Closing storage...");
|
getLog().info("Closing storage...");
|
||||||
storage.shutdown();
|
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) {
|
public void refreshAutoOp(User user, Player player) {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -209,7 +209,7 @@ public class BukkitConnectionListener implements Listener {
|
|||||||
|
|
||||||
// Remove the custom permissible
|
// Remove the custom permissible
|
||||||
try {
|
try {
|
||||||
Injector.unInject(player, true, true);
|
Injector.unInject(player, true);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,6 @@ import org.bukkit.permissions.PermissionAttachment;
|
|||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
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}.
|
* Injects a {@link LPPermissible} into a {@link Player}.
|
||||||
@ -47,7 +44,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
*/
|
*/
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class Injector {
|
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
|
* All permission checks made on standard Bukkit objects are effectively proxied to a
|
||||||
@ -123,13 +119,9 @@ public class Injector {
|
|||||||
// Setup the new permissible
|
// Setup the new permissible
|
||||||
newPermissible.getActive().set(true);
|
newPermissible.getActive().set(true);
|
||||||
newPermissible.setOldPermissible(oldPermissible);
|
newPermissible.setOldPermissible(oldPermissible);
|
||||||
newPermissible.updateSubscriptionsAsync();
|
|
||||||
|
|
||||||
// inject the new instance
|
// inject the new instance
|
||||||
HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, newPermissible);
|
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 player the player to uninject from
|
||||||
* @param dummy if the replacement permissible should be a dummy.
|
* @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
|
* @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.
|
// gets the players current permissible.
|
||||||
PermissibleBase permissible = (PermissibleBase) HUMAN_ENTITY_PERMISSIBLE_FIELD.get(player);
|
PermissibleBase permissible = (PermissibleBase) HUMAN_ENTITY_PERMISSIBLE_FIELD.get(player);
|
||||||
@ -152,11 +143,6 @@ public class Injector {
|
|||||||
// clear all permissions
|
// clear all permissions
|
||||||
lpPermissible.clearPermissions();
|
lpPermissible.clearPermissions();
|
||||||
|
|
||||||
// try to unsubscribe
|
|
||||||
if (unsubscribe) {
|
|
||||||
lpPermissible.unsubscribeFromAllAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
// set to inactive
|
// set to inactive
|
||||||
lpPermissible.getActive().set(false);
|
lpPermissible.getActive().set(false);
|
||||||
|
|
||||||
@ -174,12 +160,6 @@ public class Injector {
|
|||||||
HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, newPb);
|
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.Contexts;
|
||||||
import me.lucko.luckperms.api.Tristate;
|
import me.lucko.luckperms.api.Tristate;
|
||||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
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.config.ConfigKeys;
|
||||||
import me.lucko.luckperms.common.model.User;
|
import me.lucko.luckperms.common.model.User;
|
||||||
import me.lucko.luckperms.common.verbose.CheckOrigin;
|
import me.lucko.luckperms.common.verbose.CheckOrigin;
|
||||||
@ -45,7 +44,6 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
|
|||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -78,9 +76,6 @@ public class LPPermissible extends PermissibleBase {
|
|||||||
// the luckperms plugin instance
|
// the luckperms plugin instance
|
||||||
private final LPBukkitPlugin plugin;
|
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)
|
// the players previous permissible. (the one they had before this one was injected)
|
||||||
@Setter
|
@Setter
|
||||||
private PermissibleBase oldPermissible = null;
|
private PermissibleBase oldPermissible = null;
|
||||||
@ -97,7 +92,6 @@ public class LPPermissible extends PermissibleBase {
|
|||||||
this.user = user;
|
this.user = user;
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.subscriptions = new SubscriptionManager(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.
|
* 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
|
@Override
|
||||||
public Tristate hasPermission(String permission) {
|
public Tristate hasPermission(String permission) {
|
||||||
Tristate t = defaultsProvider.hasDefault(permission, isOp);
|
Tristate t = defaultsProvider.lookup(permission, isOp);
|
||||||
if (t != Tristate.UNDEFINED) {
|
if (t != Tristate.UNDEFINED) {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ public class DefaultsProvider {
|
|||||||
* @param isOp if the player is op
|
* @param isOp if the player is op
|
||||||
* @return a tristate result
|
* @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;
|
Map<String, Boolean> map = isOp ? opDefaults : nonOpDefaults;
|
||||||
|
|
||||||
Boolean b = map.get(permission);
|
Boolean b = map.get(permission);
|
||||||
@ -126,6 +126,10 @@ public class DefaultsProvider {
|
|||||||
unregisterDefaults(nonOpDefaults, nonOpDummy, false);
|
unregisterDefaults(nonOpDefaults, nonOpDummy, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static PluginManager pm() {
|
||||||
|
return Bukkit.getServer().getPluginManager();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters defaults for a given permissible.
|
* Unregisters defaults for a given permissible.
|
||||||
*
|
*
|
||||||
@ -136,21 +140,21 @@ public class DefaultsProvider {
|
|||||||
Set<String> perms = map.keySet();
|
Set<String> perms = map.keySet();
|
||||||
|
|
||||||
for (String name : perms) {
|
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) {
|
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) {
|
for (Permission perm : defaults) {
|
||||||
String name = perm.getName().toLowerCase();
|
String name = perm.getName().toLowerCase();
|
||||||
|
|
||||||
map.put(name, true);
|
map.put(name, true);
|
||||||
Bukkit.getServer().getPluginManager().subscribeToPermission(name, p);
|
pm().subscribeToPermission(name, p);
|
||||||
|
|
||||||
// register defaults for any children too
|
// register defaults for any children too
|
||||||
calculateChildPermissions(map, p, perm.getChildren(), false);
|
calculateChildPermissions(map, p, perm.getChildren(), false);
|
||||||
@ -167,10 +171,10 @@ public class DefaultsProvider {
|
|||||||
boolean value = e.getValue() ^ invert;
|
boolean value = e.getValue() ^ invert;
|
||||||
|
|
||||||
accumulator.put(e.getKey().toLowerCase(), value);
|
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
|
// lookup any deeper children & resolve if present
|
||||||
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
|
Permission perm = pm().getPermission(e.getKey());
|
||||||
if (perm != null) {
|
if (perm != null) {
|
||||||
calculateChildPermissions(accumulator, p, perm.getChildren(), !value);
|
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) {
|
static void sendStartupBanner(Sender sender, LuckPermsPlugin plugin) {
|
||||||
sender.sendMessage(Util.color("&b __ &3 __ ___ __ __ "));
|
sender.sendMessage(Util.color("&b __ &3 __ ___ __ __ "));
|
||||||
sender.sendMessage(Util.color("&b | | | / ` |__/ &3|__) |__ |__) |\\/| /__` "));
|
sender.sendMessage(Util.color("&b | | | / ` |__/ &3|__) |__ |__) |\\/| /__` "));
|
||||||
|
Loading…
Reference in New Issue
Block a user