Rebuild default and child permission lookup maps live instead of only once when the plugin first enables

This commit is contained in:
Luck 2018-02-04 20:50:35 +00:00
parent fd937e3209
commit 173286d404
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
17 changed files with 572 additions and 533 deletions

View File

@ -129,6 +129,14 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.6.1</version>
<scope>provided</scope>
</dependency>
<!-- Spigot --> <!-- Spigot -->
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>

View File

@ -34,14 +34,15 @@ import me.lucko.luckperms.bukkit.contexts.WorldCalculator;
import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener; import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener;
import me.lucko.luckperms.bukkit.listeners.BukkitPlatformListener; 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.PermissionMapInjector;
import me.lucko.luckperms.bukkit.model.SubscriptionMapInjector;
import me.lucko.luckperms.bukkit.model.permissible.LPPermissible; import me.lucko.luckperms.bukkit.model.permissible.LPPermissible;
import me.lucko.luckperms.bukkit.model.permissible.PermissibleInjector; import me.lucko.luckperms.bukkit.model.permissible.PermissibleInjector;
import me.lucko.luckperms.bukkit.model.permissible.PermissibleMonitoringInjector; import me.lucko.luckperms.bukkit.model.permissible.PermissibleMonitoringInjector;
import me.lucko.luckperms.bukkit.processors.BukkitProcessorsSetupTask; import me.lucko.luckperms.bukkit.model.server.InjectorDefaultsMap;
import me.lucko.luckperms.bukkit.processors.ChildPermissionProvider; import me.lucko.luckperms.bukkit.model.server.InjectorPermissionMap;
import me.lucko.luckperms.bukkit.processors.DefaultsProvider; import me.lucko.luckperms.bukkit.model.server.InjectorSubscriptionMap;
import me.lucko.luckperms.bukkit.model.server.LPDefaultsMap;
import me.lucko.luckperms.bukkit.model.server.LPPermissionMap;
import me.lucko.luckperms.bukkit.model.server.LPSubscriptionMap;
import me.lucko.luckperms.bukkit.vault.VaultHookManager; import me.lucko.luckperms.bukkit.vault.VaultHookManager;
import me.lucko.luckperms.common.actionlog.LogDispatcher; import me.lucko.luckperms.common.actionlog.LogDispatcher;
import me.lucko.luckperms.common.api.ApiRegistrationUtil; import me.lucko.luckperms.common.api.ApiRegistrationUtil;
@ -86,6 +87,7 @@ import me.lucko.luckperms.common.verbose.VerboseHandler;
import org.bukkit.command.PluginCommand; import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault; import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.ServicePriority;
@ -123,8 +125,9 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private LuckPermsApiProvider apiProvider; private LuckPermsApiProvider apiProvider;
private EventFactory eventFactory; private EventFactory eventFactory;
private Logger log; private Logger log;
private DefaultsProvider defaultsProvider; private LPSubscriptionMap subscriptionMap;
private ChildPermissionProvider childPermissionProvider; private LPPermissionMap permissionMap;
private LPDefaultsMap defaultPermissionMap;
private LocaleManager localeManager; private LocaleManager localeManager;
private PluginClassLoader pluginClassLoader; private PluginClassLoader pluginClassLoader;
private DependencyManager dependencyManager; private DependencyManager dependencyManager;
@ -197,10 +200,6 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
Set<StorageType> storageTypes = storageFactory.getRequiredTypes(StorageType.H2); Set<StorageType> storageTypes = storageFactory.getRequiredTypes(StorageType.H2);
this.dependencyManager.loadStorageDependencies(storageTypes); this.dependencyManager.loadStorageDependencies(storageTypes);
// init the Bukkit model providers
this.defaultsProvider = new DefaultsProvider();
this.childPermissionProvider = new ChildPermissionProvider();
// register events // register events
BukkitConnectionListener connectionListener = new BukkitConnectionListener(this); BukkitConnectionListener connectionListener = new BukkitConnectionListener(this);
getServer().getPluginManager().registerEvents(connectionListener, this); getServer().getPluginManager().registerEvents(connectionListener, this);
@ -246,21 +245,19 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
this.contextManager.registerStaticCalculator(new LuckPermsCalculator(getConfiguration())); this.contextManager.registerStaticCalculator(new LuckPermsCalculator(getConfiguration()));
// inject our own custom permission maps // inject our own custom permission maps
SubscriptionMapInjector subscriptionMapInjector = new SubscriptionMapInjector(this); Runnable[] injectors = new Runnable[]{
subscriptionMapInjector.run(); new InjectorSubscriptionMap(this),
new InjectorPermissionMap(this),
new InjectorDefaultsMap(this)
};
PermissionMapInjector permissionMapInjector = new PermissionMapInjector(this); for (Runnable injector : injectors) {
permissionMapInjector.run(); injector.run();
// setup the bukkit processors // schedule another injection after all plugins have loaded
BukkitProcessorsSetupTask bukkitProcessorsSetupTask = new BukkitProcessorsSetupTask(this); // the entire pluginmanager instance is replaced by some plugins :(
bukkitProcessorsSetupTask.run(); this.scheduler.asyncLater(injector, 1L);
}
// schedule another injection after all plugins have loaded - the entire pluginmanager instance
// is replaced by some plugins :(
this.scheduler.asyncLater(subscriptionMapInjector, 1L);
this.scheduler.asyncLater(permissionMapInjector, 1L);
this.scheduler.syncLater(bukkitProcessorsSetupTask, 1L);
// inject verbose handlers into internal bukkit objects // inject verbose handlers into internal bukkit objects
new PermissibleMonitoringInjector(this).run(); new PermissibleMonitoringInjector(this).run();
@ -305,7 +302,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
PermissionDefault permDefault = getConfiguration().get(ConfigKeys.COMMANDS_ALLOW_OP) ? PermissionDefault.OP : PermissionDefault.FALSE; PermissionDefault permDefault = getConfiguration().get(ConfigKeys.COMMANDS_ALLOW_OP) ? PermissionDefault.OP : PermissionDefault.FALSE;
for (CommandPermission p : CommandPermission.values()) { for (CommandPermission p : CommandPermission.values()) {
pm.addPermission(new org.bukkit.permissions.Permission(p.getPermission(), permDefault)); pm.addPermission(new Permission(p.getPermission(), permDefault));
} }
} catch (Exception e) { } catch (Exception e) {
// this throws an exception if the plugin is /reloaded, grr // this throws an exception if the plugin is /reloaded, grr
@ -351,7 +348,6 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
// Switch back to the fallback executor, the bukkit one won't allow new tasks // Switch back to the fallback executor, the bukkit one won't allow new tasks
this.scheduler.setUseFallback(true); this.scheduler.setUseFallback(true);
this.defaultsProvider.close();
this.permissionVault.shutdown(); this.permissionVault.shutdown();
this.verboseHandler.shutdown(); this.verboseHandler.shutdown();
@ -375,8 +371,9 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
} }
// uninject custom maps // uninject custom maps
SubscriptionMapInjector.uninject(); InjectorSubscriptionMap.uninject();
PermissionMapInjector.uninject(); InjectorPermissionMap.uninject();
InjectorDefaultsMap.uninject();
getLog().info("Closing storage..."); getLog().info("Closing storage...");
this.storage.shutdown(); this.storage.shutdown();
@ -447,6 +444,30 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
return configFile; return configFile;
} }
public LPSubscriptionMap getSubscriptionMap() {
return this.subscriptionMap;
}
public void setSubscriptionMap(LPSubscriptionMap subscriptionMap) {
this.subscriptionMap = subscriptionMap;
}
public LPPermissionMap getPermissionMap() {
return this.permissionMap;
}
public void setPermissionMap(LPPermissionMap permissionMap) {
this.permissionMap = permissionMap;
}
public LPDefaultsMap getDefaultPermissionMap() {
return this.defaultPermissionMap;
}
public void setDefaultPermissionMap(LPDefaultsMap defaultPermissionMap) {
this.defaultPermissionMap = defaultPermissionMap;
}
@Override @Override
public Optional<InternalMessagingService> getMessagingService() { public Optional<InternalMessagingService> getMessagingService() {
return Optional.ofNullable(this.messagingService); return Optional.ofNullable(this.messagingService);
@ -627,14 +648,6 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
return this.log; return this.log;
} }
public DefaultsProvider getDefaultsProvider() {
return this.defaultsProvider;
}
public ChildPermissionProvider getChildPermissionProvider() {
return this.childPermissionProvider;
}
@Override @Override
public LocaleManager getLocaleManager() { public LocaleManager getLocaleManager() {
return this.localeManager; return this.localeManager;

View File

@ -55,7 +55,7 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new MapProcessor()); processors.add(new MapProcessor());
if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_CHILD_PERMISSIONS)) { if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_CHILD_PERMISSIONS)) {
processors.add(new ChildProcessor(this.plugin.getChildPermissionProvider())); processors.add(new ChildProcessor(this.plugin));
} }
if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) { if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) {
@ -67,7 +67,7 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
} }
if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS) && metadata.getHolderType() == HolderType.USER) { if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS) && metadata.getHolderType() == HolderType.USER) {
processors.add(new DefaultsProcessor(contexts.isOp(), this.plugin.getDefaultsProvider())); processors.add(new DefaultsProcessor(this.plugin, contexts.isOp()));
} }
return registerCalculator(new PermissionCalculator(this.plugin, metadata, processors.build())); return registerCalculator(new PermissionCalculator(this.plugin, metadata, processors.build()));

View File

@ -1,72 +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 me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.treeview.PermissionVault;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
import java.util.HashMap;
import java.util.Map;
/**
* A replacement map for the 'permissions' instance in Bukkit's SimplePluginManager.
*
* This instance allows LuckPerms to intercept calls to
* {@link PluginManager#addPermission(Permission)} and record permissions in the
* {@link PermissionVault}.
*
* Injected by {@link PermissionMapInjector}.
*/
public class LPPermissionMap extends HashMap<String, Permission> {
final LuckPermsPlugin plugin;
public LPPermissionMap(LuckPermsPlugin plugin, Map<String, Permission> existingData) {
this.plugin = plugin;
putAll(existingData);
}
@Override
public Permission put(String key, Permission value) {
this.plugin.getPermissionVault().offer(key);
return super.put(key, value);
}
@Override
public void putAll(Map<? extends String, ? extends Permission> m) {
this.plugin.getPermissionVault().offerAll(m.keySet());
super.putAll(m);
}
@Override
public Permission putIfAbsent(String key, Permission value) {
this.plugin.getPermissionVault().offer(key);
return super.putIfAbsent(key, value);
}
}

View File

@ -1,62 +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.dummy;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import java.util.Collections;
import java.util.Set;
public class DummyPermissible implements Permissible {
private final Runnable onRefresh;
public DummyPermissible(Runnable onRefresh) {
this.onRefresh = onRefresh;
}
@Override
public void recalculatePermissions() {
this.onRefresh.run();
}
@Override public Set<PermissionAttachmentInfo> getEffectivePermissions() { return Collections.emptySet(); }
@Override public boolean isPermissionSet(String name) { return false; }
@Override public boolean isPermissionSet(Permission perm) { return false; }
@Override public boolean hasPermission(String name) { return false; }
@Override public boolean hasPermission(Permission perm) { return false; }
@Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { return null; }
@Override public PermissionAttachment addAttachment(Plugin plugin) { return null; }
@Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { return null; }
@Override public PermissionAttachment addAttachment(Plugin plugin, int ticks) { return null; }
@Override public void removeAttachment(PermissionAttachment attachment) {}
@Override public boolean isOp() { return false; }
@Override public void setOp(boolean value) {}
}

View File

@ -0,0 +1,119 @@
/*
* 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.server;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.SimplePluginManager;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* Injects a {@link LPDefaultsMap} info the {@link PluginManager}.
*/
public class InjectorDefaultsMap implements Runnable {
private static final Field DEFAULT_PERMISSIONS_FIELD;
static {
Field permissionsField = null;
try {
permissionsField = SimplePluginManager.class.getDeclaredField("defaultPerms");
permissionsField.setAccessible(true);
} catch (Exception e) {
// ignore
}
DEFAULT_PERMISSIONS_FIELD = permissionsField;
}
private final LPBukkitPlugin plugin;
public InjectorDefaultsMap(LPBukkitPlugin plugin) {
this.plugin = plugin;
}
@Override
public void run() {
try {
LPDefaultsMap ret = inject();
if (ret != null) {
this.plugin.setDefaultPermissionMap(ret);
}
} catch (Exception e) {
this.plugin.getLog().severe("Exception occurred whilst injecting LuckPerms Default Permission map.");
e.printStackTrace();
}
}
private LPDefaultsMap inject() throws Exception {
Objects.requireNonNull(DEFAULT_PERMISSIONS_FIELD, "DEFAULT_PERMISSIONS_FIELD");
PluginManager pluginManager = this.plugin.getServer().getPluginManager();
if (!(pluginManager instanceof SimplePluginManager)) {
this.plugin.getLog().severe("PluginManager instance is not a 'SimplePluginManager', instead: " + pluginManager.getClass());
this.plugin.getLog().severe("Unable to inject LuckPerms Default Permission map.");
return null;
}
Object map = DEFAULT_PERMISSIONS_FIELD.get(pluginManager);
if (map instanceof LPDefaultsMap && ((LPDefaultsMap) map).plugin == this.plugin) {
return null;
}
//noinspection unchecked
Map<Boolean, Set<Permission>> castedMap = (Map<Boolean, Set<Permission>>) map;
// make a new map & inject it
LPDefaultsMap newMap = new LPDefaultsMap(this.plugin, castedMap);
DEFAULT_PERMISSIONS_FIELD.set(pluginManager, newMap);
return newMap;
}
public static void uninject() {
try {
Objects.requireNonNull(DEFAULT_PERMISSIONS_FIELD, "DEFAULT_PERMISSIONS_FIELD");
PluginManager pluginManager = Bukkit.getServer().getPluginManager();
if (!(pluginManager instanceof SimplePluginManager)) {
return;
}
Object map = DEFAULT_PERMISSIONS_FIELD.get(pluginManager);
if (map instanceof LPDefaultsMap) {
LPDefaultsMap lpMap = (LPDefaultsMap) map;
DEFAULT_PERMISSIONS_FIELD.set(pluginManager, new HashMap<>(lpMap));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package me.lucko.luckperms.bukkit.model; package me.lucko.luckperms.bukkit.model.server;
import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.bukkit.LPBukkitPlugin;
@ -40,7 +40,7 @@ import java.util.Objects;
/** /**
* Injects a {@link LPPermissionMap} into the {@link PluginManager}. * Injects a {@link LPPermissionMap} into the {@link PluginManager}.
*/ */
public class PermissionMapInjector implements Runnable { public class InjectorPermissionMap implements Runnable {
private static final Field PERMISSIONS_FIELD; private static final Field PERMISSIONS_FIELD;
static { static {
@ -56,43 +56,45 @@ public class PermissionMapInjector implements Runnable {
private final LPBukkitPlugin plugin; private final LPBukkitPlugin plugin;
public PermissionMapInjector(LPBukkitPlugin plugin) { public InjectorPermissionMap(LPBukkitPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
} }
@Override @Override
public void run() { public void run() {
try { try {
inject(); LPPermissionMap ret = inject();
if (ret != null) {
this.plugin.setPermissionMap(ret);
}
} catch (Exception e) { } catch (Exception e) {
this.plugin.getLog().severe("Exception occurred whilst injecting LuckPerms Permission map."); this.plugin.getLog().severe("Exception occurred whilst injecting LuckPerms Permission map.");
e.printStackTrace(); e.printStackTrace();
} }
} }
private void inject() throws Exception { private LPPermissionMap inject() throws Exception {
Objects.requireNonNull(PERMISSIONS_FIELD, "PERMISSIONS_FIELD"); Objects.requireNonNull(PERMISSIONS_FIELD, "PERMISSIONS_FIELD");
PluginManager pluginManager = this.plugin.getServer().getPluginManager(); PluginManager pluginManager = this.plugin.getServer().getPluginManager();
if (!(pluginManager instanceof SimplePluginManager)) { if (!(pluginManager instanceof SimplePluginManager)) {
this.plugin.getLog().severe("PluginManager instance is not a 'SimplePluginManager', instead: " + pluginManager.getClass()); this.plugin.getLog().severe("PluginManager instance is not a 'SimplePluginManager', instead: " + pluginManager.getClass());
this.plugin.getLog().severe("Unable to inject LuckPerms Permission map."); this.plugin.getLog().severe("Unable to inject LuckPerms Permission map.");
return; return null;
} }
Object map = PERMISSIONS_FIELD.get(pluginManager); Object map = PERMISSIONS_FIELD.get(pluginManager);
if (map instanceof LPPermissionMap && ((LPPermissionMap) map).plugin == this.plugin) { if (map instanceof LPPermissionMap && ((LPPermissionMap) map).plugin == this.plugin) {
return; return null;
} }
//noinspection unchecked //noinspection unchecked
Map<String, Permission> castedMap = (Map<String, Permission>) map; Map<String, Permission> castedMap = (Map<String, Permission>) map;
// make a new map // make a new map & inject it
LPPermissionMap newMap = new LPPermissionMap(this.plugin, castedMap); LPPermissionMap newMap = new LPPermissionMap(this.plugin, castedMap);
// inject it
PERMISSIONS_FIELD.set(pluginManager, newMap); PERMISSIONS_FIELD.set(pluginManager, newMap);
return newMap;
} }
public static void uninject() { public static void uninject() {

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package me.lucko.luckperms.bukkit.model; package me.lucko.luckperms.bukkit.model.server;
import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.bukkit.LPBukkitPlugin;
@ -39,7 +39,7 @@ import java.util.Objects;
/** /**
* Injects a {@link LPSubscriptionMap} into the {@link PluginManager}. * Injects a {@link LPSubscriptionMap} into the {@link PluginManager}.
*/ */
public class SubscriptionMapInjector implements Runnable { public class InjectorSubscriptionMap implements Runnable {
private static final Field PERM_SUBS_FIELD; private static final Field PERM_SUBS_FIELD;
static { static {
@ -55,34 +55,37 @@ public class SubscriptionMapInjector implements Runnable {
private final LPBukkitPlugin plugin; private final LPBukkitPlugin plugin;
public SubscriptionMapInjector(LPBukkitPlugin plugin) { public InjectorSubscriptionMap(LPBukkitPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
} }
@Override @Override
public void run() { public void run() {
try { try {
inject(); LPSubscriptionMap ret = inject();
if (ret != null) {
this.plugin.setSubscriptionMap(ret);
}
} catch (Exception e) { } catch (Exception e) {
this.plugin.getLog().severe("Exception occurred whilst injecting LuckPerms Permission Subscription map."); this.plugin.getLog().severe("Exception occurred whilst injecting LuckPerms Permission Subscription map.");
e.printStackTrace(); e.printStackTrace();
} }
} }
private void inject() throws Exception { private LPSubscriptionMap inject() throws Exception {
Objects.requireNonNull(PERM_SUBS_FIELD, "PERM_SUBS_FIELD"); Objects.requireNonNull(PERM_SUBS_FIELD, "PERM_SUBS_FIELD");
PluginManager pluginManager = this.plugin.getServer().getPluginManager(); PluginManager pluginManager = this.plugin.getServer().getPluginManager();
if (!(pluginManager instanceof SimplePluginManager)) { if (!(pluginManager instanceof SimplePluginManager)) {
this.plugin.getLog().severe("PluginManager instance is not a 'SimplePluginManager', instead: " + pluginManager.getClass()); this.plugin.getLog().severe("PluginManager instance is not a 'SimplePluginManager', instead: " + pluginManager.getClass());
this.plugin.getLog().severe("Unable to inject LuckPerms Permission Subscription map."); this.plugin.getLog().severe("Unable to inject LuckPerms Permission Subscription map.");
return; return null;
} }
Object map = PERM_SUBS_FIELD.get(pluginManager); Object map = PERM_SUBS_FIELD.get(pluginManager);
if (map instanceof LPSubscriptionMap) { if (map instanceof LPSubscriptionMap) {
if (((LPSubscriptionMap) map).plugin == this.plugin) { if (((LPSubscriptionMap) map).plugin == this.plugin) {
return; return null;
} }
map = ((LPSubscriptionMap) map).detach(); map = ((LPSubscriptionMap) map).detach();
@ -91,11 +94,10 @@ public class SubscriptionMapInjector implements Runnable {
//noinspection unchecked //noinspection unchecked
Map<String, Map<Permissible, Boolean>> castedMap = (Map<String, Map<Permissible, Boolean>>) map; Map<String, Map<Permissible, Boolean>> castedMap = (Map<String, Map<Permissible, Boolean>>) map;
// make a new subscription map // make a new subscription map & inject it
LPSubscriptionMap newMap = new LPSubscriptionMap(this.plugin, castedMap); LPSubscriptionMap newMap = new LPSubscriptionMap(this.plugin, castedMap);
// inject it
PERM_SUBS_FIELD.set(pluginManager, newMap); PERM_SUBS_FIELD.set(pluginManager, newMap);
return newMap;
} }
public static void uninject() { public static void uninject() {

View File

@ -0,0 +1,197 @@
/*
* 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.server;
import com.google.common.collect.ForwardingSet;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull;
/**
* A replacement map for the 'defaultPerms' instance in Bukkit's SimplePluginManager.
*
* This instance allows LuckPerms to intercept calls to
* {@link PluginManager#addPermission(Permission)}, specifically regarding
* the default nature of the permission.
*
* Injected by {@link InjectorDefaultsMap}.
*/
public class LPDefaultsMap implements Map<Boolean, Set<Permission>> {
// keyset for all instances
private static final Set<Boolean> KEY_SET = ImmutableSet.of(Boolean.TRUE, Boolean.FALSE);
// the plugin
final LPBukkitPlugin plugin;
// the two values in the map
private final Set<Permission> opSet = new DefaultPermissionSet(true);
private final Set<Permission> nonOpSet = new DefaultPermissionSet(false);
// fully resolved defaults (accounts for child permissions too)
private Map<String, Boolean> resolvedOpDefaults = ImmutableMap.of();
private Map<String, Boolean> resolvedNonOpDefaults = ImmutableMap.of();
// #values and #entrySet results - both immutable
private final Collection<Set<Permission>> values = ImmutableList.of(this.opSet, this.nonOpSet);
private final Set<Entry<Boolean, Set<Permission>>> entrySet = ImmutableSet.of(
Maps.immutableEntry(Boolean.TRUE, this.opSet),
Maps.immutableEntry(Boolean.FALSE, this.nonOpSet)
);
public LPDefaultsMap(LPBukkitPlugin plugin, Map<Boolean, Set<Permission>> existingData) {
this.plugin = plugin;
this.opSet.addAll(existingData.getOrDefault(Boolean.TRUE, Collections.emptySet()));
this.nonOpSet.addAll(existingData.getOrDefault(Boolean.FALSE, Collections.emptySet()));
refreshOp();
refreshNonOp();
}
public Set<Permission> getOpPermissions() {
return this.opSet;
}
public Set<Permission> getNonOpPermissions() {
return this.nonOpSet;
}
/**
* Queries whether a given permission should be granted by default.
*
* @param permission the permission to query
* @param isOp if the player is op
* @return a tristate result
*/
public Tristate lookupDefaultPermission(String permission, boolean isOp) {
Map<String, Boolean> map = isOp ? this.resolvedOpDefaults : this.resolvedNonOpDefaults;
return Tristate.fromNullableBoolean(map.get(permission));
}
private void refresh(boolean op) {
if (op) {
refreshOp();
} else {
refreshNonOp();
}
}
/**
* Refreshes the op data in this provider.
*/
private void refreshOp() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : getOpPermissions()) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
this.resolvedOpDefaults = ImmutableMap.copyOf(builder);
}
/**
* Refreshes the non op data in this provider.
*/
private void refreshNonOp() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : getNonOpPermissions()) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
this.resolvedNonOpDefaults = ImmutableMap.copyOf(builder);
}
@Override
public Set<Permission> get(Object key) {
boolean b = (boolean) key;
return b ? this.opSet : this.nonOpSet;
}
// return wrappers around this map impl
@Nonnull @Override public Collection<Set<Permission>> values() { return this.values; }
@Nonnull @Override public Set<Entry<Boolean, Set<Permission>>> entrySet() { return this.entrySet; }
@Nonnull @Override public Set<Boolean> keySet() { return KEY_SET; }
// return accurate results for the Map spec
@Override public int size() { return 2; }
@Override public boolean isEmpty() { return false; }
@Override public boolean containsKey(Object key) { return key instanceof Boolean; }
@Override public boolean containsValue(Object value) { return value == this.opSet || value == this.nonOpSet; }
// throw unsupported operation exceptions
@Override public Set<Permission> put(Boolean key, Set<Permission> value) { throw new UnsupportedOperationException(); }
@Override public Set<Permission> remove(Object key) { throw new UnsupportedOperationException(); }
@Override public void putAll(@Nonnull Map<? extends Boolean, ? extends Set<Permission>> m) { throw new UnsupportedOperationException(); }
@Override public void clear() { throw new UnsupportedOperationException(); }
private final class DefaultPermissionSet extends ForwardingSet<Permission> {
private final Set<Permission> delegate = ConcurrentHashMap.newKeySet();
private final boolean op;
private DefaultPermissionSet(boolean op) {
this.op = op;
}
@Override
protected Set<Permission> delegate() {
return this.delegate;
}
@Override
public boolean add(@Nonnull Permission element) {
boolean ret = super.add(element);
refresh(this.op);
return ret;
}
@Override
public boolean addAll(@Nonnull Collection<? extends Permission> collection) {
boolean ret = super.addAll(collection);
refresh(this.op);
return ret;
}
}
}

View File

@ -0,0 +1,155 @@
/*
* 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.server;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.treeview.PermissionVault;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* A replacement map for the 'permissions' instance in Bukkit's SimplePluginManager.
*
* This instance allows LuckPerms to intercept calls to
* {@link PluginManager#addPermission(Permission)} and record permissions in the
* {@link PermissionVault}.
*
* It also allows us to pre-determine child permission relationships.
*
* Injected by {@link InjectorPermissionMap}.
*/
public class LPPermissionMap extends ForwardingMap<String, Permission> {
// Uses perm.getName().toLowerCase(java.util.Locale.ENGLISH); to determine the key
private final Map<String, Permission> delegate = new ConcurrentHashMap<>();
// cache from permission --> children
private final LoadingCache<String, Map<String, Boolean>> trueChildPermissions = Caffeine.newBuilder()
.build(new ChildPermissionResolver(true));
private final LoadingCache<String, Map<String, Boolean>> falseChildPermissions = Caffeine.newBuilder()
.build(new ChildPermissionResolver(false));
/**
* The plugin instance
*/
final LuckPermsPlugin plugin;
public LPPermissionMap(LuckPermsPlugin plugin, Map<String, Permission> existingData) {
this.plugin = plugin;
putAll(existingData);
}
public Map<String, Boolean> getChildPermissions(String permission, boolean value) {
return value ? this.trueChildPermissions.get(permission) : this.falseChildPermissions.get(permission);
}
private void update() {
this.trueChildPermissions.invalidateAll();
this.falseChildPermissions.invalidateAll();
}
@Override
protected Map<String, Permission> delegate() {
return this.delegate;
}
@Override
public Permission put(@Nonnull String key, @Nonnull Permission value) {
this.plugin.getPermissionVault().offer(key);
Permission ret = super.put(key, value);
update();
return ret;
}
@Override
public void putAll(@Nonnull Map<? extends String, ? extends Permission> m) {
this.plugin.getPermissionVault().offerAll(m.keySet());
super.putAll(m);
update();
}
@Override
public Permission putIfAbsent(String key, Permission value) {
this.plugin.getPermissionVault().offer(key);
Permission ret = super.putIfAbsent(key, value);
update();
return ret;
}
private final class ChildPermissionResolver implements CacheLoader<String, Map<String, Boolean>> {
private final boolean value;
private ChildPermissionResolver(boolean value) {
this.value = value;
}
@CheckForNull
@Override
public Map<String, Boolean> load(@Nonnull String key) {
Map<String, Boolean> children = new HashMap<>();
resolveChildren(children, Collections.singletonMap(key, this.value), false);
children.remove(key, this.value);
return ImmutableMap.copyOf(children);
}
}
private void resolveChildren(Map<String, Boolean> accumulator, Map<String, Boolean> children, boolean invert) {
// iterate through the current known children.
// the first time this method is called for a given permission, the children map will contain only the permission itself.
for (Map.Entry<String, Boolean> e : children.entrySet()) {
if (accumulator.containsKey(e.getKey())) {
continue; // Prevent infinite loops
}
// xor the value using the parent (bukkit logic, not mine)
boolean value = e.getValue() ^ invert;
accumulator.put(e.getKey().toLowerCase(), value);
// lookup any deeper children & resolve if present
Permission perm = this.delegate.get(e.getKey());
if (perm != null) {
resolveChildren(accumulator, perm.getChildren(), !value);
}
}
}
}

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package me.lucko.luckperms.bukkit.model; package me.lucko.luckperms.bukkit.model.server;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -66,7 +66,7 @@ import javax.annotation.Nonnull;
* *
* This class implements option 2 above. It is preferred because it is faster & uses less memory * This class implements option 2 above. It is preferred because it is faster & uses less memory
* *
* Injected by {@link SubscriptionMapInjector}. * Injected by {@link InjectorSubscriptionMap}.
*/ */
public class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>> { public class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>> {

View File

@ -1,45 +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.processors;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
/**
* Performs the initial setup for Bukkit permission processors
*/
public class BukkitProcessorsSetupTask implements Runnable {
private final LPBukkitPlugin plugin;
public BukkitProcessorsSetupTask(LPBukkitPlugin plugin) {
this.plugin = plugin;
}
@Override
public void run() {
this.plugin.getDefaultsProvider().refresh();
this.plugin.getChildPermissionProvider().setup();
}
}

View File

@ -1,107 +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.processors;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Holds child permissions registered on the platform.
*
* The data stored in this class is pulled from the data in {@link PluginManager#getPermissions()}.
*
* The former method is not thread safe, so we populate this class when the server starts to get all of the data
* in a form which is easily queryable & thread safe.
*
* The data is resolved early, so the represented child permissions are a "deep" lookup of permissions.
*/
public class ChildPermissionProvider {
// in the format: permission+value ===> children (a map of child permissions)
private ImmutableMap<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> permissions = ImmutableMap.of();
public void setup() {
ImmutableMap.Builder<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> permissions = ImmutableMap.builder();
// iterate all permissions registered on the platform & resolve.
for (Permission permission : Bukkit.getServer().getPluginManager().getPermissions()) {
resolve(permissions, permission, true);
resolve(permissions, permission, false);
}
this.permissions = permissions.build();
}
private static void resolve(ImmutableMap.Builder<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> accumulator, Permission permission, boolean value) {
// accumulator for the child permissions being looked up
Map<String, Boolean> children = new HashMap<>();
// resolve children for the permission, so pass a map containing just the permission being looked up.
resolveChildren(children, Collections.singletonMap(permission.getName(), value), false);
// remove self
children.remove(permission.getName(), value);
// only register the children if there are any.
if (!children.isEmpty()) {
accumulator.put(Maps.immutableEntry(permission.getName().toLowerCase(), value), ImmutableMap.copyOf(children));
}
}
private static void resolveChildren(Map<String, Boolean> accumulator, Map<String, Boolean> children, boolean invert) {
// iterate through the current known children.
// the first time this method is called for a given permission, the children map will contain only the permission itself.
for (Map.Entry<String, Boolean> e : children.entrySet()) {
if (accumulator.containsKey(e.getKey())) {
continue; // Prevent infinite loops
}
// xor the value using the parent (bukkit logic, not mine)
boolean value = e.getValue() ^ invert;
accumulator.put(e.getKey().toLowerCase(), value);
// lookup any deeper children & resolve if present
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
if (perm != null) {
resolveChildren(accumulator, perm.getChildren(), !value);
}
}
}
public ImmutableMap<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> getPermissions() {
return this.permissions;
}
}

View File

@ -25,9 +25,8 @@
package me.lucko.luckperms.bukkit.processors; package me.lucko.luckperms.bukkit.processors;
import com.google.common.collect.Maps;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.processors.AbstractPermissionProcessor; import me.lucko.luckperms.common.processors.AbstractPermissionProcessor;
import me.lucko.luckperms.common.processors.PermissionProcessor; import me.lucko.luckperms.common.processors.PermissionProcessor;
@ -39,11 +38,11 @@ import java.util.concurrent.ConcurrentHashMap;
* Permission Processor for Bukkits "child" permission system. * Permission Processor for Bukkits "child" permission system.
*/ */
public class ChildProcessor extends AbstractPermissionProcessor implements PermissionProcessor { public class ChildProcessor extends AbstractPermissionProcessor implements PermissionProcessor {
private final ChildPermissionProvider provider; private final LPBukkitPlugin plugin;
private Map<String, Boolean> childPermissions = Collections.emptyMap(); private Map<String, Boolean> childPermissions = Collections.emptyMap();
public ChildProcessor(ChildPermissionProvider provider) { public ChildProcessor(LPBukkitPlugin plugin) {
this.provider = provider; this.plugin = plugin;
} }
@Override @Override
@ -55,7 +54,7 @@ public class ChildProcessor extends AbstractPermissionProcessor implements Permi
public void refresh() { public void refresh() {
Map<String, Boolean> builder = new ConcurrentHashMap<>(); Map<String, Boolean> builder = new ConcurrentHashMap<>();
for (Map.Entry<String, Boolean> e : this.sourceMap.entrySet()) { for (Map.Entry<String, Boolean> e : this.sourceMap.entrySet()) {
Map<String, Boolean> children = this.provider.getPermissions().get(Maps.immutableEntry(e.getKey(), e.getValue())); Map<String, Boolean> children = this.plugin.getPermissionMap().getChildPermissions(e.getKey(), e.getValue());
if (children != null) { if (children != null) {
builder.putAll(children); builder.putAll(children);
} }

View File

@ -26,31 +26,31 @@
package me.lucko.luckperms.bukkit.processors; package me.lucko.luckperms.bukkit.processors;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.processors.PermissionProcessor; import me.lucko.luckperms.common.processors.PermissionProcessor;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission; import org.bukkit.permissions.Permission;
/** /**
* Permission Processor for Bukkits "default" permission system. * Permission Processor for Bukkits "default" permission system.
*/ */
public class DefaultsProcessor implements PermissionProcessor { public class DefaultsProcessor implements PermissionProcessor {
private final LPBukkitPlugin plugin;
private final boolean isOp; private final boolean isOp;
private final DefaultsProvider defaultsProvider;
public DefaultsProcessor(boolean isOp, DefaultsProvider defaultsProvider) { public DefaultsProcessor(LPBukkitPlugin plugin, boolean isOp) {
this.plugin = plugin;
this.isOp = isOp; this.isOp = isOp;
this.defaultsProvider = defaultsProvider;
} }
@Override @Override
public Tristate hasPermission(String permission) { public Tristate hasPermission(String permission) {
Tristate t = this.defaultsProvider.lookup(permission, this.isOp); Tristate t = this.plugin.getDefaultPermissionMap().lookupDefaultPermission(permission, this.isOp);
if (t != Tristate.UNDEFINED) { if (t != Tristate.UNDEFINED) {
return t; return t;
} }
Permission defPerm = Bukkit.getServer().getPluginManager().getPermission(permission); Permission defPerm = this.plugin.getPermissionMap().get(permission);
return defPerm == null ? Tristate.UNDEFINED : Tristate.fromBoolean(defPerm.getDefault().getValue(this.isOp)); return defPerm == null ? Tristate.UNDEFINED : Tristate.fromBoolean(defPerm.getDefault().getValue(this.isOp));
} }
} }

View File

@ -1,169 +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.processors;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.bukkit.model.dummy.DummyPermissible;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Holds default permissions registered on the platform.
*
* The data stored in this class is pulled from the data in {@link PluginManager#getDefaultPermissions(boolean)}.
*
* The former method is not thread safe, so we populate this class when the server starts to get all of the data
* in a form which is easily queryable & thread safe.
*
* The {@link DummyPermissible}s are registered with Bukkit, so we can listen for any
* changes to default permissions.
*/
public class DefaultsProvider {
// defaults for opped players
private Map<String, Boolean> opDefaults = ImmutableMap.of();
private final DummyPermissible opDummy = new DummyPermissible(this::refreshOp);
// defaults for non-opped players
private Map<String, Boolean> nonOpDefaults = ImmutableMap.of();
private final DummyPermissible nonOpDummy = new DummyPermissible(this::refreshNonOp);
/**
* Refreshes the data in this provider.
*/
public void refresh() {
refreshOp();
refreshNonOp();
}
/**
* Queries whether a given permission should be granted by default.
*
* @param permission the permission to query
* @param isOp if the player is op
* @return a tristate result
*/
public Tristate lookup(String permission, boolean isOp) {
Map<String, Boolean> map = isOp ? this.opDefaults : this.nonOpDefaults;
return Tristate.fromNullableBoolean(map.get(permission));
}
/**
* Refreshes the op data in this provider.
*/
private void refreshOp() {
unregisterDefaults(this.opDefaults, this.opDummy, true);
Map<String, Boolean> builder = new HashMap<>();
calculateDefaults(builder, this.opDummy, true);
this.opDefaults = ImmutableMap.copyOf(builder);
}
/**
* Refreshes the non op data in this provider.
*/
private void refreshNonOp() {
unregisterDefaults(this.nonOpDefaults, this.nonOpDummy, false);
Map<String, Boolean> builder = new HashMap<>();
calculateDefaults(builder, this.nonOpDummy, false);
this.nonOpDefaults = ImmutableMap.copyOf(builder);
}
/**
* Unregisters the dummy permissibles with Bukkit.
*/
public void close() {
unregisterDefaults(this.opDefaults, this.opDummy, true);
unregisterDefaults(this.nonOpDefaults, this.nonOpDummy, false);
}
private static PluginManager pm() {
return Bukkit.getServer().getPluginManager();
}
/**
* Unregisters defaults for a given permissible.
*
* @param map the map of current defaults
* @param p the permissible
*/
private static void unregisterDefaults(Map<String, Boolean> map, DummyPermissible p, boolean op) {
Set<String> perms = map.keySet();
for (String name : perms) {
pm().unsubscribeFromPermission(name, p);
}
pm().unsubscribeFromDefaultPerms(op, p);
}
private static void calculateDefaults(Map<String, Boolean> map, DummyPermissible p, boolean op) {
pm().subscribeToDefaultPerms(op, p);
Set<Permission> defaults = pm().getDefaultPermissions(op);
for (Permission perm : defaults) {
String name = perm.getName().toLowerCase();
map.put(name, true);
pm().subscribeToPermission(name, p);
// register defaults for any children too
calculateChildPermissions(map, p, perm.getChildren(), false);
}
}
private static void calculateChildPermissions(Map<String, Boolean> accumulator, DummyPermissible p, Map<String, Boolean> children, boolean invert) {
for (Map.Entry<String, Boolean> e : children.entrySet()) {
if (accumulator.containsKey(e.getKey())) {
continue; // Prevent infinite loops
}
// xor the value using the parent (bukkit logic, not mine)
boolean value = e.getValue() ^ invert;
accumulator.put(e.getKey().toLowerCase(), value);
pm().subscribeToPermission(e.getKey(), p);
// lookup any deeper children & resolve if present
Permission perm = pm().getPermission(e.getKey());
if (perm != null) {
calculateChildPermissions(accumulator, p, perm.getChildren(), !value);
}
}
}
}

View File

@ -8,16 +8,15 @@ main: me.lucko.luckperms.bukkit.LPBukkitPlugin
load: STARTUP load: STARTUP
# This means that all plugins that (soft-)depend on Vault, depend on LuckPerms too. # This means that all plugins that (soft-)depend on Vault, depend on LuckPerms too.
# It in turn fixes issues where plugins using Vault cache the provided instance when their plugin enables, or # It in turn fixes issues where plugins using Vault cache the provided instance
# when they check for the presence of a service provider, before LuckPerms has enabled. # when their plugin enables, or when they check for the presence of a service
# provider, before LuckPerms has enabled.
loadbefore: [Vault] loadbefore: [Vault]
# Soft depend on LilyPad for messaging service impl
softdepend: [LilyPad-Connect] softdepend: [LilyPad-Connect]
commands: commands:
luckperms: luckperms:
description: Manage permissions description: Manage permissions
aliases: [lp, perm, perms, permission, permissions] aliases: [lp, perm, perms, permission, permissions]
# Permissions are registered programmatically instead of here.
# See the last method in me.lucko.luckperms.bukkit.LPBukkitPlugin