mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2025-01-29 19:51:40 +01:00
Cleanup & document a number of Bukkit impl classes
This commit is contained in:
parent
845367e847
commit
e42cc101cc
@ -206,7 +206,11 @@ public class BukkitListener implements Listener {
|
||||
final Player player = e.getPlayer();
|
||||
|
||||
// Remove the custom permissible
|
||||
Injector.unInject(player, true, true);
|
||||
try {
|
||||
Injector.unInject(player, true, true);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
// Handle auto op
|
||||
if (plugin.getConfiguration().get(ConfigKeys.AUTO_OP)) {
|
||||
|
@ -32,6 +32,7 @@ import me.lucko.luckperms.api.Logger;
|
||||
import me.lucko.luckperms.api.LuckPermsApi;
|
||||
import me.lucko.luckperms.api.PlatformType;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.api.context.MutableContextSet;
|
||||
import me.lucko.luckperms.bukkit.messaging.BungeeMessagingService;
|
||||
import me.lucko.luckperms.bukkit.messaging.LilyPadMessagingService;
|
||||
@ -39,7 +40,7 @@ import me.lucko.luckperms.bukkit.model.ChildPermissionProvider;
|
||||
import me.lucko.luckperms.bukkit.model.DefaultsProvider;
|
||||
import me.lucko.luckperms.bukkit.model.Injector;
|
||||
import me.lucko.luckperms.bukkit.model.LPPermissible;
|
||||
import me.lucko.luckperms.bukkit.vault.VaultHook;
|
||||
import me.lucko.luckperms.bukkit.vault.VaultHookManager;
|
||||
import me.lucko.luckperms.common.api.ApiHandler;
|
||||
import me.lucko.luckperms.common.api.ApiProvider;
|
||||
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
|
||||
@ -111,7 +112,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
private long startTime;
|
||||
private LPBukkitScheduler scheduler;
|
||||
private BukkitCommand commandManager;
|
||||
private VaultHook vaultHook = null;
|
||||
private VaultHookManager vaultHookManager = null;
|
||||
private LuckPermsConfiguration configuration;
|
||||
private UserManager userManager;
|
||||
private GroupManager groupManager;
|
||||
@ -288,13 +289,26 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
calculatorFactory = new BukkitCalculatorFactory(this);
|
||||
cachedStateManager = new CachedStateManager(this);
|
||||
|
||||
contextManager = new ContextManager<>();
|
||||
contextManager = new ContextManager<Player>() {
|
||||
@Override
|
||||
public Contexts formContexts(Player player, ImmutableContextSet contextSet) {
|
||||
return new Contexts(
|
||||
contextSet,
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
player.isOp()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
worldCalculator = new WorldCalculator(this);
|
||||
contextManager.registerCalculator(worldCalculator);
|
||||
|
||||
StaticCalculator<Player> staticCalculator = new StaticCalculator<>(getConfiguration());
|
||||
contextManager.registerCalculator(staticCalculator);
|
||||
contextManager.registerStaticCalculator(staticCalculator);
|
||||
contextManager.registerCalculator(staticCalculator, true);
|
||||
|
||||
// Provide vault support
|
||||
tryVaultHook(false);
|
||||
@ -368,7 +382,12 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
verboseHandler.setShutdown(true);
|
||||
|
||||
for (Player player : getServer().getOnlinePlayers()) {
|
||||
Injector.unInject(player, false, false);
|
||||
try {
|
||||
Injector.unInject(player, false, false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
|
||||
player.setOp(false);
|
||||
}
|
||||
@ -395,8 +414,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
ApiHandler.unregisterProvider();
|
||||
getServer().getServicesManager().unregisterAll(this);
|
||||
|
||||
if (vaultHook != null) {
|
||||
vaultHook.unhook(this);
|
||||
if (vaultHookManager != null) {
|
||||
vaultHookManager.unhook(this);
|
||||
}
|
||||
|
||||
getLog().info("Shutting down internal scheduler...");
|
||||
@ -408,7 +427,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
|
||||
// Null everything
|
||||
ignoringLogs = null;
|
||||
vaultHook = null;
|
||||
vaultHookManager = null;
|
||||
configuration = null;
|
||||
userManager = null;
|
||||
groupManager = null;
|
||||
@ -434,18 +453,18 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
}
|
||||
|
||||
public void tryVaultHook(boolean force) {
|
||||
if (vaultHook != null) {
|
||||
if (vaultHookManager != null) {
|
||||
return; // already hooked
|
||||
}
|
||||
|
||||
try {
|
||||
if (force || getServer().getPluginManager().isPluginEnabled("Vault")) {
|
||||
vaultHook = new VaultHook();
|
||||
vaultHook.hook(this);
|
||||
vaultHookManager = new VaultHookManager();
|
||||
vaultHookManager.hook(this);
|
||||
getLog().info("Registered Vault permission & chat hook.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
vaultHook = null;
|
||||
vaultHookManager = null;
|
||||
getLog().severe("Error occurred whilst hooking into Vault.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -531,15 +550,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
return new Contexts(
|
||||
getContextManager().getApplicableContext(player),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
player.isOp()
|
||||
);
|
||||
return contextManager.getApplicableContexts(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -645,7 +656,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
@Override
|
||||
public LinkedHashMap<String, Object> getExtraInfo() {
|
||||
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("Vault Enabled", vaultHook != null);
|
||||
map.put("Vault Enabled", vaultHookManager != null);
|
||||
map.put("Bukkit Defaults count", defaultsProvider.size());
|
||||
map.put("Bukkit Child Permissions count", childPermissionProvider.getPermissions().size());
|
||||
return map;
|
||||
|
@ -36,6 +36,9 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Permission Processor for permissions set to a player via permission attachments.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class AttachmentProcessor implements PermissionProcessor {
|
||||
|
||||
|
@ -36,6 +36,9 @@ import me.lucko.luckperms.common.calculators.PermissionProcessor;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Permission Processor for Bukkits "child" permission system.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ChildProcessor implements PermissionProcessor {
|
||||
private final ChildPermissionProvider provider;
|
||||
|
@ -36,6 +36,9 @@ import org.bukkit.permissions.Permission;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Permission Processor for Bukkits "default" permission system.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class DefaultsProcessor implements PermissionProcessor {
|
||||
private final boolean isOp;
|
||||
|
@ -33,6 +33,9 @@ import net.kyori.text.serializer.ComponentSerializer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Sends a json message to a command sender.
|
||||
*/
|
||||
public class MessageHandler {
|
||||
private final BukkitJsonMessageHandler bukkitHandler;
|
||||
private final SpigotJsonMessageHandler spigotHandler;
|
||||
|
@ -32,52 +32,71 @@ 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)
|
||||
@Getter
|
||||
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()) {
|
||||
// handle true
|
||||
Map<String, Boolean> trueChildren = new HashMap<>();
|
||||
resolveChildren(trueChildren, Collections.singletonMap(permission.getName(), true), false);
|
||||
trueChildren.remove(permission.getName(), true);
|
||||
if (!trueChildren.isEmpty()) {
|
||||
permissions.put(Maps.immutableEntry(permission.getName().toLowerCase(), true), ImmutableMap.copyOf(trueChildren));
|
||||
}
|
||||
|
||||
// handle false
|
||||
Map<String, Boolean> falseChildren = new HashMap<>();
|
||||
resolveChildren(falseChildren, Collections.singletonMap(permission.getName(), false), false);
|
||||
falseChildren.remove(permission.getName(), false);
|
||||
if (!falseChildren.isEmpty()) {
|
||||
permissions.put(Maps.immutableEntry(permission.getName().toLowerCase(), false), ImmutableMap.copyOf(falseChildren));
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
|
||||
// xor the value using the parent (bukkit logic, not mine)
|
||||
boolean value = e.getValue() ^ invert;
|
||||
String lName = e.getKey().toLowerCase();
|
||||
|
||||
accumulator.put(lName, value);
|
||||
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);
|
||||
}
|
||||
|
@ -33,49 +33,50 @@ import me.lucko.luckperms.api.Tristate;
|
||||
|
||||
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
|
||||
@Getter
|
||||
private Map<String, Boolean> opDefaults = ImmutableMap.of();
|
||||
private final DummyPermissible opDummy = new DummyPermissible(this::refreshOp);
|
||||
|
||||
|
||||
// defaults for non-opped players
|
||||
@Getter
|
||||
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();
|
||||
}
|
||||
|
||||
private void refreshOp() {
|
||||
unregisterDefaults(opDefaults, opDummy);
|
||||
|
||||
Map<String, Boolean> builder = new HashMap<>();
|
||||
calculateDefaults(builder, opDummy, true);
|
||||
|
||||
opDefaults = ImmutableMap.copyOf(builder);
|
||||
}
|
||||
|
||||
private void refreshNonOp() {
|
||||
unregisterDefaults(nonOpDefaults, nonOpDummy);
|
||||
|
||||
Map<String, Boolean> builder = new HashMap<>();
|
||||
calculateDefaults(builder, nonOpDummy, false);
|
||||
|
||||
nonOpDefaults = ImmutableMap.copyOf(builder);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
unregisterDefaults(opDefaults, opDummy);
|
||||
unregisterDefaults(nonOpDefaults, nonOpDummy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 hasDefault(String permission, boolean isOp) {
|
||||
Map<String, Boolean> map = isOp ? opDefaults : nonOpDefaults;
|
||||
|
||||
@ -83,44 +84,94 @@ public class DefaultsProvider {
|
||||
return b == null ? Tristate.UNDEFINED : Tristate.fromBoolean(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of default permissions held by the provider.
|
||||
*
|
||||
* @return the number of permissions held
|
||||
*/
|
||||
public int size() {
|
||||
return opDefaults.size() + nonOpDefaults.size();
|
||||
}
|
||||
|
||||
private static void unregisterDefaults(Map<String, Boolean> map, DummyPermissible p) {
|
||||
/**
|
||||
* Refreshes the op data in this provider.
|
||||
*/
|
||||
private void refreshOp() {
|
||||
unregisterDefaults(opDefaults, opDummy, true);
|
||||
|
||||
Map<String, Boolean> builder = new HashMap<>();
|
||||
calculateDefaults(builder, opDummy, true);
|
||||
|
||||
opDefaults = ImmutableMap.copyOf(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the non op data in this provider.
|
||||
*/
|
||||
private void refreshNonOp() {
|
||||
unregisterDefaults(nonOpDefaults, nonOpDummy, false);
|
||||
|
||||
Map<String, Boolean> builder = new HashMap<>();
|
||||
calculateDefaults(builder, nonOpDummy, false);
|
||||
|
||||
nonOpDefaults = ImmutableMap.copyOf(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the dummy permissibles with Bukkit.
|
||||
*/
|
||||
public void close() {
|
||||
unregisterDefaults(opDefaults, opDummy, true);
|
||||
unregisterDefaults(nonOpDefaults, nonOpDummy, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, p);
|
||||
}
|
||||
|
||||
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(false, p);
|
||||
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(true, p);
|
||||
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(op, p);
|
||||
}
|
||||
|
||||
private static void calculateDefaults(Map<String, Boolean> map, DummyPermissible p, boolean op) {
|
||||
Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(op);
|
||||
Bukkit.getServer().getPluginManager().subscribeToDefaultPerms(op, p);
|
||||
|
||||
Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(op);
|
||||
for (Permission perm : defaults) {
|
||||
String name = perm.getName().toLowerCase();
|
||||
|
||||
map.put(name, true);
|
||||
Bukkit.getServer().getPluginManager().subscribeToPermission(name, p);
|
||||
|
||||
// register defaults for any children too
|
||||
calculateChildPermissions(map, p, perm.getChildren(), false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void calculateChildPermissions(Map<String, Boolean> map, DummyPermissible p, Map<String, Boolean> children, boolean invert) {
|
||||
private static void calculateChildPermissions(Map<String, Boolean> accumulator, DummyPermissible p, Map<String, Boolean> children, boolean invert) {
|
||||
for (Map.Entry<String, Boolean> e : children.entrySet()) {
|
||||
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
|
||||
boolean value = e.getValue() ^ invert;
|
||||
String lName = e.getKey().toLowerCase();
|
||||
if (accumulator.containsKey(e.getKey())) {
|
||||
continue; // Prevent infinite loops
|
||||
}
|
||||
|
||||
map.put(lName, value);
|
||||
// xor the value using the parent (bukkit logic, not mine)
|
||||
boolean value = e.getValue() ^ invert;
|
||||
|
||||
accumulator.put(e.getKey().toLowerCase(), value);
|
||||
Bukkit.getServer().getPluginManager().subscribeToPermission(e.getKey(), p);
|
||||
|
||||
// lookup any deeper children & resolve if present
|
||||
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
|
||||
if (perm != null) {
|
||||
calculateChildPermissions(map, p, perm.getChildren(), !value);
|
||||
calculateChildPermissions(accumulator, p, perm.getChildren(), !value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,31 +40,49 @@ 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}.
|
||||
*
|
||||
* This allows LuckPerms to directly intercept permission checks and take over all handling of
|
||||
* checks made by plugins.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@UtilityClass
|
||||
public class Injector {
|
||||
private static final Map<UUID, LPPermissible> INJECTED_PERMISSIBLES = new ConcurrentHashMap<>();
|
||||
|
||||
private static Field humanEntityField;
|
||||
private static Field permissibleAttachmentsField;
|
||||
private static Throwable cachedThrowable = null;
|
||||
/**
|
||||
* All permission checks made on standard Bukkit objects are effectively proxied to a
|
||||
* {@link PermissibleBase} object, held as a parameter on the object.
|
||||
*
|
||||
* This field is where the permissible is stored on a HumanEntity.
|
||||
*/
|
||||
private static Field humanEntityPermissibleField;
|
||||
|
||||
/**
|
||||
* The field where attachments are stored on a permissible base.
|
||||
*/
|
||||
private static Field permissibleBaseAttachmentsField;
|
||||
|
||||
private static Throwable cachedThrowable = null;
|
||||
static {
|
||||
try {
|
||||
// Catch all. If this setup doesn't fully complete without
|
||||
// exceptions, then the Injector will not work.
|
||||
|
||||
// Try to load the permissible field.
|
||||
try {
|
||||
// craftbukkit
|
||||
humanEntityField = ReflectionUtil.obcClass("entity.CraftHumanEntity").getDeclaredField("perm");
|
||||
humanEntityField.setAccessible(true);
|
||||
humanEntityPermissibleField = ReflectionUtil.obcClass("entity.CraftHumanEntity").getDeclaredField("perm");
|
||||
humanEntityPermissibleField.setAccessible(true);
|
||||
|
||||
} catch (Exception e) {
|
||||
// glowstone
|
||||
humanEntityField = Class.forName("net.glowstone.entity.GlowHumanEntity").getDeclaredField("permissions");
|
||||
humanEntityField.setAccessible(true);
|
||||
humanEntityPermissibleField = Class.forName("net.glowstone.entity.GlowHumanEntity").getDeclaredField("permissions");
|
||||
humanEntityPermissibleField.setAccessible(true);
|
||||
}
|
||||
|
||||
permissibleAttachmentsField = PermissibleBase.class.getDeclaredField("attachments");
|
||||
permissibleAttachmentsField.setAccessible(true);
|
||||
// Try to load the attachments field.
|
||||
permissibleBaseAttachmentsField = PermissibleBase.class.getDeclaredField("attachments");
|
||||
permissibleBaseAttachmentsField.setAccessible(true);
|
||||
|
||||
} catch (Throwable t) {
|
||||
cachedThrowable = t;
|
||||
@ -72,82 +90,107 @@ public class Injector {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean inject(Player player, LPPermissible lpPermissible) {
|
||||
/**
|
||||
* Injects a {@link LPPermissible} into a {@link Player}.
|
||||
*
|
||||
* @param player the player to inject into
|
||||
* @param newPermissible the permissible to inject
|
||||
* @throws Exception propagates any exceptions which were thrown during injection
|
||||
*/
|
||||
public static void inject(Player player, LPPermissible newPermissible) throws Exception {
|
||||
|
||||
// make sure the class inited without errors, otherwise, print a trace
|
||||
if (cachedThrowable != null) {
|
||||
cachedThrowable.printStackTrace();
|
||||
return false;
|
||||
throw new RuntimeException("Injector did not init successfully.", cachedThrowable);
|
||||
}
|
||||
|
||||
try {
|
||||
PermissibleBase existing = (PermissibleBase) humanEntityField.get(player);
|
||||
if (existing instanceof LPPermissible) {
|
||||
// uh oh
|
||||
throw new IllegalStateException("LPPermissible already injected into player " + player.toString());
|
||||
}
|
||||
// get the existing PermissibleBase held by the player
|
||||
PermissibleBase oldPermissible = (PermissibleBase) humanEntityPermissibleField.get(player);
|
||||
|
||||
// Move attachments over from the old permissible.
|
||||
List<PermissionAttachment> attachments = (List<PermissionAttachment>) permissibleAttachmentsField.get(existing);
|
||||
lpPermissible.addAttachments(attachments);
|
||||
attachments.clear();
|
||||
existing.clearPermissions();
|
||||
|
||||
lpPermissible.getActive().set(true);
|
||||
lpPermissible.recalculatePermissions(false);
|
||||
lpPermissible.setOldPermissible(existing);
|
||||
|
||||
lpPermissible.updateSubscriptionsAsync();
|
||||
|
||||
humanEntityField.set(player, lpPermissible);
|
||||
INJECTED_PERMISSIBLES.put(player.getUniqueId(), lpPermissible);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
// seems we have already injected into this player.
|
||||
if (oldPermissible instanceof LPPermissible) {
|
||||
throw new IllegalStateException("LPPermissible already injected into player " + player.toString());
|
||||
}
|
||||
|
||||
// Move attachments over from the old permissible
|
||||
|
||||
//noinspection unchecked
|
||||
List<PermissionAttachment> attachments = (List<PermissionAttachment>) permissibleBaseAttachmentsField.get(oldPermissible);
|
||||
|
||||
newPermissible.addAttachments(attachments);
|
||||
attachments.clear();
|
||||
oldPermissible.clearPermissions();
|
||||
|
||||
// Setup the new permissible
|
||||
newPermissible.getActive().set(true);
|
||||
newPermissible.recalculatePermissions(false);
|
||||
newPermissible.setOldPermissible(oldPermissible);
|
||||
newPermissible.updateSubscriptionsAsync();
|
||||
|
||||
// inject the new instance
|
||||
humanEntityPermissibleField.set(player, newPermissible);
|
||||
|
||||
// register the injection with the map
|
||||
INJECTED_PERMISSIBLES.put(player.getUniqueId(), newPermissible);
|
||||
}
|
||||
|
||||
public static boolean unInject(Player player, boolean dummy, boolean unsubscribe) {
|
||||
/**
|
||||
* Uninjects a {@link LPPermissible} from a {@link Player}.
|
||||
*
|
||||
* @param player the player to uninject from
|
||||
* @param dummy if the replacement permissible should be a dummy.
|
||||
* @param unsubscribe if the extracted permissible should unsubscribe itself. see {@link SubscriptionManager}.
|
||||
* @throws Exception propagates any exceptions which were thrown during uninjection
|
||||
*/
|
||||
public static void unInject(Player player, boolean dummy, boolean unsubscribe) throws Exception {
|
||||
// make sure the class inited without errors, otherwise, print a trace
|
||||
if (cachedThrowable != null) {
|
||||
cachedThrowable.printStackTrace();
|
||||
return false;
|
||||
throw new RuntimeException("Injector did not init successfully.", cachedThrowable);
|
||||
}
|
||||
|
||||
try {
|
||||
PermissibleBase permissible = (PermissibleBase) humanEntityField.get(player);
|
||||
if (permissible instanceof LPPermissible) {
|
||||
// gets the players current permissible.
|
||||
PermissibleBase permissible = (PermissibleBase) humanEntityPermissibleField.get(player);
|
||||
|
||||
permissible.clearPermissions();
|
||||
// only uninject if the permissible was a luckperms one.
|
||||
if (permissible instanceof LPPermissible) {
|
||||
LPPermissible lpPermissible = ((LPPermissible) permissible);
|
||||
|
||||
if (unsubscribe) {
|
||||
((LPPermissible) permissible).unsubscribeFromAllAsync();
|
||||
}
|
||||
// clear all permissions
|
||||
lpPermissible.clearPermissions();
|
||||
|
||||
((LPPermissible) permissible).getActive().set(false);
|
||||
|
||||
if (dummy) {
|
||||
humanEntityField.set(player, new DummyPermissibleBase());
|
||||
} else {
|
||||
LPPermissible lpp = ((LPPermissible) permissible);
|
||||
List<PermissionAttachment> attachments = lpp.getAttachments();
|
||||
|
||||
PermissibleBase newPb = lpp.getOldPermissible();
|
||||
if (newPb == null) {
|
||||
newPb = new PermissibleBase(player);
|
||||
}
|
||||
|
||||
List<PermissionAttachment> newAttachments = (List<PermissionAttachment>) permissibleAttachmentsField.get(newPb);
|
||||
newAttachments.addAll(attachments);
|
||||
attachments.clear();
|
||||
|
||||
humanEntityField.set(player, newPb);
|
||||
}
|
||||
// try to unsubscribe
|
||||
if (unsubscribe) {
|
||||
lpPermissible.unsubscribeFromAllAsync();
|
||||
}
|
||||
|
||||
// set to inactive
|
||||
lpPermissible.getActive().set(false);
|
||||
|
||||
// handle the replacement permissible.
|
||||
if (dummy) {
|
||||
// just inject a dummy class. this is used when we know the player is about to quit the server.
|
||||
humanEntityPermissibleField.set(player, new DummyPermissibleBase());
|
||||
|
||||
} else {
|
||||
// otherwise, inject the permissible they had when we first injected.
|
||||
|
||||
List<PermissionAttachment> lpAttachments = lpPermissible.getAttachments();
|
||||
|
||||
PermissibleBase newPb = lpPermissible.getOldPermissible();
|
||||
if (newPb == null) {
|
||||
newPb = new PermissibleBase(player);
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
List<PermissionAttachment> newPbAttachments = (List<PermissionAttachment>) permissibleBaseAttachmentsField.get(newPb);
|
||||
newPbAttachments.addAll(lpAttachments);
|
||||
lpAttachments.clear();
|
||||
|
||||
humanEntityPermissibleField.set(player, newPb);
|
||||
}
|
||||
INJECTED_PERMISSIBLES.remove(player.getUniqueId());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
INJECTED_PERMISSIBLES.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public static LPPermissible getPermissible(UUID uuid) {
|
||||
|
@ -45,9 +45,10 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||
import org.bukkit.permissions.PermissionRemovedExecutor;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -57,24 +58,47 @@ import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Modified PermissibleBase for LuckPerms
|
||||
* PermissibleBase for LuckPerms.
|
||||
*
|
||||
* This class overrides all methods defined in PermissibleBase, and provides custom handling
|
||||
* from LuckPerms.
|
||||
*
|
||||
* This means that all permission checks made for a player are handled directly by the plugin.
|
||||
* Method behaviour is retained, but alternate implementation is used.
|
||||
*
|
||||
* "Hot" method calls, (namely #hasPermission) are significantly faster than the base implementation.
|
||||
*
|
||||
* This class is **thread safe**. This means that when LuckPerms is installed on the server,
|
||||
* is is safe to call Player#hasPermission asynchronously.
|
||||
*/
|
||||
@Getter
|
||||
public class LPPermissible extends PermissibleBase {
|
||||
|
||||
// the LuckPerms user this permissible references.
|
||||
private final User user;
|
||||
|
||||
// the player this permissible is injected into.
|
||||
private final Player parent;
|
||||
|
||||
// the luckperms plugin instance
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
// the subscription manager, handling the players permission subscriptions.
|
||||
private final SubscriptionManager subscriptions;
|
||||
|
||||
// the players previous permissible. (the one they had before this one was injected)
|
||||
@Setter
|
||||
private PermissibleBase oldPermissible = null;
|
||||
|
||||
// if the permissible is currently active.
|
||||
private final AtomicBoolean active = new AtomicBoolean(false);
|
||||
|
||||
// Attachment stuff.
|
||||
// the permissions registered by PermissionAttachments.
|
||||
// stored in this format, as that's what is used by #getEffectivePermissions
|
||||
private final Map<String, PermissionAttachmentInfo> attachmentPermissions = new ConcurrentHashMap<>();
|
||||
private final List<PermissionAttachment> attachments = Collections.synchronizedList(new LinkedList<>());
|
||||
|
||||
// the attachments hooked onto the permissible.
|
||||
private final List<PermissionAttachment> attachments = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
public LPPermissible(@NonNull Player parent, @NonNull User user, @NonNull LPBukkitPlugin plugin) {
|
||||
super(parent);
|
||||
@ -84,6 +108,49 @@ public class LPPermissible extends PermissibleBase {
|
||||
this.subscriptions = new SubscriptionManager(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(@NonNull String permission) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission);
|
||||
return ts != Tristate.UNDEFINED || Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(@NonNull Permission permission) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName());
|
||||
if (ts != Tristate.UNDEFINED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
return Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
} else {
|
||||
return permission.getDefault().getValue(isOp());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull String permission) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission);
|
||||
return ts != Tristate.UNDEFINED ? ts.asBoolean() : Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull Permission permission) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName());
|
||||
if (ts != Tristate.UNDEFINED) {
|
||||
return ts.asBoolean();
|
||||
}
|
||||
|
||||
if (!plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
return Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
} else {
|
||||
return permission.getDefault().getValue(isOp());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the players subscriptions asynchronously
|
||||
*/
|
||||
public void updateSubscriptionsAsync() {
|
||||
if (!active.get()) {
|
||||
return;
|
||||
@ -92,18 +159,20 @@ public class LPPermissible extends PermissibleBase {
|
||||
plugin.doAsync(this::updateSubscriptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the players subscriptions
|
||||
*/
|
||||
public void updateSubscriptions() {
|
||||
if (!active.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserCache cache = user.getUserData();
|
||||
if (cache == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 (parent.isOp()) {
|
||||
ent.addAll(plugin.getDefaultsProvider().getOpDefaults().keySet());
|
||||
@ -115,28 +184,37 @@ public class LPPermissible extends PermissibleBase {
|
||||
subscriptions.subscribe(ent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from all permissions asynchronously
|
||||
*/
|
||||
public void unsubscribeFromAllAsync() {
|
||||
plugin.doAsync(this::unsubscribeFromAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from all permissions
|
||||
*/
|
||||
public void unsubscribeFromAll() {
|
||||
subscriptions.subscribe(Collections.emptySet());
|
||||
}
|
||||
|
||||
public void addAttachments(List<PermissionAttachment> attachments) {
|
||||
/**
|
||||
* Adds attachments to this permissible.
|
||||
*
|
||||
* @param attachments the attachments to add
|
||||
*/
|
||||
public void addAttachments(Collection<PermissionAttachment> attachments) {
|
||||
this.attachments.addAll(attachments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a {@link Contexts} instance for the player.
|
||||
* Values are determined using the plugins ContextManager.
|
||||
*
|
||||
* @return the calculated contexts for the player.
|
||||
*/
|
||||
public Contexts calculateContexts() {
|
||||
return new Contexts(
|
||||
plugin.getContextManager().getApplicableContext(parent),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
parent.isOp()
|
||||
);
|
||||
return plugin.getContextManager().getApplicableContexts(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -144,46 +222,6 @@ public class LPPermissible extends PermissibleBase {
|
||||
parent.setOp(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(@NonNull String name) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(name);
|
||||
return ts != Tristate.UNDEFINED || Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(@NonNull Permission perm) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(perm.getName());
|
||||
if (ts != Tristate.UNDEFINED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
return Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
} else {
|
||||
return perm.getDefault().getValue(isOp());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull String name) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(name);
|
||||
return ts != Tristate.UNDEFINED ? ts.asBoolean() : Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull Permission perm) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(perm.getName());
|
||||
if (ts != Tristate.UNDEFINED) {
|
||||
return ts.asBoolean();
|
||||
}
|
||||
|
||||
if (!plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
return Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
} else {
|
||||
return perm.getDefault().getValue(isOp());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
|
||||
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||
|
@ -40,11 +40,27 @@ import me.lucko.luckperms.common.utils.ExtractedContexts;
|
||||
|
||||
import net.milkbowl.vault.chat.Chat;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* LuckPerms Vault Chat implementation
|
||||
* All user lookups are cached.
|
||||
* An implementation of the Vault {@link Chat} API using LuckPerms.
|
||||
*
|
||||
* Methods which change the state of data objects are likely to return immediately.
|
||||
*
|
||||
* LuckPerms is a multithreaded permissions plugin, and some actions require considerable
|
||||
* time to execute. (database queries, re-population of caches, etc) In these cases, the
|
||||
* methods will return immediately and the change will be executed asynchronously.
|
||||
*
|
||||
* Users of the Vault API expect these methods to be "main thread friendly", so unfortunately,
|
||||
* we have to favour so called "performance" for consistency. The Vault API really wasn't designed
|
||||
* with database backed permission plugins in mind. :(
|
||||
*
|
||||
* The methods which query offline players will explicitly FAIL if the corresponding player is not online.
|
||||
* We cannot risk blocking the main thread to load in their data. Again, this is due to crap Vault
|
||||
* design. There is nothing I can do about it.
|
||||
*/
|
||||
public class VaultChatHook extends Chat {
|
||||
private final VaultPermissionHook perms;
|
||||
@ -61,37 +77,209 @@ public class VaultChatHook extends Chat {
|
||||
public boolean isEnabled() {
|
||||
return perms.isEnabled();
|
||||
}
|
||||
|
||||
private User getUser(String username) {
|
||||
Player player = Bukkit.getPlayerExact(username);
|
||||
return player == null ? null : perms.getPlugin().getUserManager().getIfLoaded(perms.getPlugin().getUuidCache().getUUID(player.getUniqueId()));
|
||||
}
|
||||
|
||||
private void setMeta(PermissionHolder holder, String world, String node, String value) {
|
||||
String finalWorld = perms.isIgnoreWorld() ? null : world;
|
||||
if (holder == null || node.equals("")) return;
|
||||
@Override
|
||||
public String getPlayerPrefix(String world, @NonNull String player) {
|
||||
final User user = getUser(player);
|
||||
return getUserChatMeta(user, ChatMetaType.PREFIX, world);
|
||||
}
|
||||
|
||||
perms.log("Setting meta: '" + node + "' for " + holder.getObjectName() + " on world " + world + ", server " + perms.getServer());
|
||||
@Override
|
||||
public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) {
|
||||
final User user = getUser(player);
|
||||
setChatMeta(user, ChatMetaType.PREFIX, prefix, world);
|
||||
}
|
||||
|
||||
perms.getScheduler().execute(() -> {
|
||||
holder.removeIf(n -> n.isMeta() && n.getMeta().getKey().equals(node));
|
||||
@Override
|
||||
public String getPlayerSuffix(String world, @NonNull String player) {
|
||||
final User user = getUser(player);
|
||||
return getUserChatMeta(user, ChatMetaType.SUFFIX, world);
|
||||
}
|
||||
|
||||
Node.Builder metaNode = NodeFactory.makeMetaNode(node, value).setValue(true);
|
||||
if (!perms.getServer().equalsIgnoreCase("global")) {
|
||||
metaNode.setServer(perms.getServer());
|
||||
}
|
||||
if (finalWorld != null && !finalWorld.equals("") && !finalWorld.equals("global")) {
|
||||
metaNode.setWorld(finalWorld);
|
||||
}
|
||||
@Override
|
||||
public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) {
|
||||
final User user = getUser(player);
|
||||
setChatMeta(user, ChatMetaType.SUFFIX, suffix, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupPrefix(String world, @NonNull String group) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
return getGroupChatMeta(g, ChatMetaType.PREFIX, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setChatMeta(g, ChatMetaType.PREFIX, prefix, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupSuffix(String world, @NonNull String group) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
return getGroupChatMeta(g, ChatMetaType.SUFFIX, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setChatMeta(g, ChatMetaType.SUFFIX, suffix, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int defaultValue) {
|
||||
final User user = getUser(player);
|
||||
try {
|
||||
return Integer.parseInt(getUserMeta(user, node, world, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int value) {
|
||||
final User user = getUser(player);
|
||||
setMeta(user, node, String.valueOf(value), world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
try {
|
||||
return Integer.parseInt(getGroupMeta(g, node, world, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int value) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setMeta(g, node, String.valueOf(value), world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) {
|
||||
final User user = getUser(player);
|
||||
try {
|
||||
return Double.parseDouble(getUserMeta(user, node, world, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double value) {
|
||||
final User user = getUser(player);
|
||||
setMeta(user, node, String.valueOf(value), world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
try {
|
||||
return Double.parseDouble(getGroupMeta(g, node, world, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double value) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setMeta(g, node, String.valueOf(value), world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) {
|
||||
final User user = getUser(player);
|
||||
String s = getUserMeta(user, node, world, String.valueOf(defaultValue));
|
||||
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean value) {
|
||||
final User user = getUser(player);
|
||||
setMeta(user, node, String.valueOf(value), world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
String s = getGroupMeta(g, node, world, String.valueOf(defaultValue));
|
||||
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean value) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setMeta(g, node, String.valueOf(value), world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerInfoString(String world, @NonNull String player, @NonNull String node, String defaultValue) {
|
||||
final User user = getUser(player);
|
||||
return getUserMeta(user, node, world, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerInfoString(String world, @NonNull String player, @NonNull String node, String value) {
|
||||
final User user = getUser(player);
|
||||
setMeta(user, node, value, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupInfoString(String world, @NonNull String group, @NonNull String node, String defaultValue) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
return getGroupMeta(g, node, world, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupInfoString(String world, @NonNull String group, @NonNull String node, String value) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setMeta(g, node, value, world);
|
||||
}
|
||||
|
||||
private void setMeta(PermissionHolder holder, String key, String value, String world) {
|
||||
if (holder == null || key.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String finalWorld = perms.correctWorld(world);
|
||||
perms.log("Setting meta: '" + key + "' for " + holder.getObjectName() + " on world " + world + ", server " + perms.getServer());
|
||||
|
||||
perms.getExecutor().execute(() -> {
|
||||
holder.removeIf(n -> n.isMeta() && n.getMeta().getKey().equals(key));
|
||||
|
||||
Node.Builder metaNode = NodeFactory.makeMetaNode(key, value).setValue(true);
|
||||
metaNode.setServer(perms.getServer());
|
||||
metaNode.setWorld(finalWorld);
|
||||
|
||||
holder.setPermission(metaNode.build());
|
||||
perms.save(holder);
|
||||
perms.holderSave(holder);
|
||||
});
|
||||
}
|
||||
|
||||
private void setChatMeta(ChatMetaType type, PermissionHolder holder, String value, String world) {
|
||||
String finalWorld = perms.isIgnoreWorld() ? null : world;
|
||||
if (holder == null) return;
|
||||
if (value.equals("")) return;
|
||||
private void setChatMeta(PermissionHolder holder, ChatMetaType type, String value, String world) {
|
||||
if (holder == null || value.equals("")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String finalWorld = perms.correctWorld(world);
|
||||
perms.log("Setting " + type.name().toLowerCase() + " for " + holder.getObjectName() + " on world " + world + ", server " + perms.getServer());
|
||||
|
||||
perms.getScheduler().execute(() -> {
|
||||
perms.getExecutor().execute(() -> {
|
||||
// remove all prefixes/suffixes directly set on the user/group
|
||||
holder.removeIf(type::matches);
|
||||
|
||||
@ -101,32 +289,32 @@ public class VaultChatHook extends Chat {
|
||||
.mapToInt(e -> e).max().orElse(0) + 10;
|
||||
|
||||
Node.Builder chatMetaNode = NodeFactory.makeChatMetaNode(type, priority, value);
|
||||
if (!perms.getServer().equalsIgnoreCase("global")) {
|
||||
chatMetaNode.setServer(perms.getServer());
|
||||
}
|
||||
if (finalWorld != null && !finalWorld.equals("") && !finalWorld.equals("global")) {
|
||||
chatMetaNode.setWorld(finalWorld);
|
||||
}
|
||||
chatMetaNode.setServer(perms.getServer());
|
||||
chatMetaNode.setWorld(finalWorld);
|
||||
|
||||
holder.setPermission(chatMetaNode.build());
|
||||
perms.save(holder);
|
||||
perms.holderSave(holder);
|
||||
});
|
||||
}
|
||||
|
||||
private String getUserMeta(User user, String world, String node, String defaultValue) {
|
||||
if (user == null) return defaultValue;
|
||||
world = perms.isIgnoreWorld() ? null : world;
|
||||
private String getUserMeta(User user, String node, String world, String defaultValue) {
|
||||
if (user == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
world = perms.correctWorld(world);
|
||||
perms.log("Getting meta: '" + node + "' for user " + user.getFriendlyName() + " on world " + world + ", server " + perms.getServer());
|
||||
|
||||
String ret = user.getUserData().getMetaData(perms.createContextForWorldLookup(perms.getPlugin().getPlayer(user), world)).getMeta().get(node);
|
||||
return ret != null ? ret : defaultValue;
|
||||
}
|
||||
|
||||
private String getUserChatMeta(ChatMetaType type, User user, String world) {
|
||||
if (user == null) return "";
|
||||
world = perms.isIgnoreWorld() ? null : world;
|
||||
private String getUserChatMeta(User user, ChatMetaType type, String world) {
|
||||
if (user == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
world = perms.correctWorld(world);
|
||||
perms.log("Getting " + type.name().toLowerCase() + " for user " + user.getFriendlyName() + " on world " + world + ", server " + perms.getServer());
|
||||
|
||||
MetaData data = user.getUserData().getMetaData(perms.createContextForWorldLookup(perms.getPlugin().getPlayer(user), world));
|
||||
@ -134,10 +322,12 @@ public class VaultChatHook extends Chat {
|
||||
return ret != null ? ret : "";
|
||||
}
|
||||
|
||||
private String getGroupMeta(Group group, String world, String node, String defaultValue) {
|
||||
world = perms.isIgnoreWorld() ? null : world;
|
||||
if (group == null || node.equals("")) return defaultValue;
|
||||
private String getGroupMeta(Group group, String node, String world, String defaultValue) {
|
||||
if (group == null || node.equals("")) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
world = perms.correctWorld(world);
|
||||
perms.log("Getting meta: '" + node + "' for group " + group.getName() + " on world " + world + ", server " + perms.getServer());
|
||||
|
||||
for (Node n : group.mergePermissionsToList()) {
|
||||
@ -154,10 +344,12 @@ public class VaultChatHook extends Chat {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private String getGroupChatMeta(ChatMetaType type, Group group, String world) {
|
||||
world = perms.isIgnoreWorld() ? null : world;
|
||||
if (group == null) return "";
|
||||
private String getGroupChatMeta(Group group, ChatMetaType type, String world) {
|
||||
if (group == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
world = perms.correctWorld(world);
|
||||
perms.log("Getting " + type.name().toLowerCase() + " for group " + group + " on world " + world + ", server " + perms.getServer());
|
||||
|
||||
int priority = Integer.MIN_VALUE;
|
||||
@ -179,172 +371,4 @@ public class VaultChatHook extends Chat {
|
||||
return meta != null ? meta : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerPrefix(String world, @NonNull String player) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
return getUserChatMeta(ChatMetaType.PREFIX, user, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
setChatMeta(ChatMetaType.PREFIX, user, prefix, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerSuffix(String world, @NonNull String player) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
return getUserChatMeta(ChatMetaType.SUFFIX, user, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
setChatMeta(ChatMetaType.SUFFIX, user, suffix, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupPrefix(String world, @NonNull String group) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
return getGroupChatMeta(ChatMetaType.PREFIX, g, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setChatMeta(ChatMetaType.PREFIX, g, prefix, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupSuffix(String world, @NonNull String group) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
return getGroupChatMeta(ChatMetaType.SUFFIX, g, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setChatMeta(ChatMetaType.SUFFIX, g, suffix, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int defaultValue) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
try {
|
||||
return Integer.parseInt(getUserMeta(user, world, node, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int value) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
setMeta(user, world, node, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
try {
|
||||
return Integer.parseInt(getGroupMeta(g, world, node, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int value) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setMeta(g, world, node, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
try {
|
||||
return Double.parseDouble(getUserMeta(user, world, node, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double value) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
setMeta(user, world, node, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
try {
|
||||
return Double.parseDouble(getGroupMeta(g, world, node, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double value) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setMeta(g, world, node, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
String s = getUserMeta(user, world, node, String.valueOf(defaultValue));
|
||||
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean value) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
setMeta(user, world, node, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
String s = getGroupMeta(g, world, node, String.valueOf(defaultValue));
|
||||
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean value) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setMeta(g, world, node, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerInfoString(String world, @NonNull String player, @NonNull String node, String defaultValue) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
return getUserMeta(user, world, node, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerInfoString(String world, @NonNull String player, @NonNull String node, String value) {
|
||||
final User user = perms.getPlugin().getUserManager().getByUsername(player);
|
||||
setMeta(user, world, node, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupInfoString(String world, @NonNull String group, @NonNull String node, String defaultValue) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
return getGroupMeta(g, world, node, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupInfoString(String world, @NonNull String group, @NonNull String node, String value) {
|
||||
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
|
||||
setMeta(g, world, node, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,12 +36,12 @@ import java.util.concurrent.Executor;
|
||||
/**
|
||||
* Sequential executor for Vault modifications
|
||||
*/
|
||||
public class VaultScheduler implements Runnable, Executor {
|
||||
public class VaultExecutor implements Runnable, Executor {
|
||||
|
||||
private BukkitTask task = null;
|
||||
private final List<Runnable> tasks = new ArrayList<>();
|
||||
|
||||
public VaultScheduler(LPBukkitPlugin plugin) {
|
||||
public VaultExecutor(LPBukkitPlugin plugin) {
|
||||
task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this, 1L, 1L);
|
||||
}
|
||||
|
||||
@ -60,15 +60,21 @@ public class VaultScheduler implements Runnable, Executor {
|
||||
return;
|
||||
}
|
||||
|
||||
toRun = new ArrayList<>();
|
||||
toRun = new ArrayList<>(tasks.size());
|
||||
toRun.addAll(tasks);
|
||||
tasks.clear();
|
||||
}
|
||||
|
||||
toRun.forEach(Runnable::run);
|
||||
for (Runnable runnable : toRun) {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelTask() {
|
||||
public void close() {
|
||||
if (task != null) {
|
||||
task.cancel();
|
||||
task = null;
|
@ -39,10 +39,16 @@ import org.bukkit.plugin.ServicesManager;
|
||||
* Handles hooking with the Vault API
|
||||
*/
|
||||
@Getter
|
||||
public class VaultHook {
|
||||
public class VaultHookManager {
|
||||
private VaultChatHook chatHook = null;
|
||||
private VaultPermissionHook permissionHook = null;
|
||||
|
||||
/**
|
||||
* Registers the LuckPerms implementation of {@link Permission} and {@link Chat} with
|
||||
* the service manager.
|
||||
*
|
||||
* @param plugin the plugin
|
||||
*/
|
||||
public void hook(LPBukkitPlugin plugin) {
|
||||
try {
|
||||
if (permissionHook == null) {
|
||||
@ -62,19 +68,31 @@ public class VaultHook {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the LuckPerms Vault hooks, if present.
|
||||
*
|
||||
* @param plugin the plugin
|
||||
*/
|
||||
public void unhook(LPBukkitPlugin plugin) {
|
||||
final ServicesManager sm = plugin.getServer().getServicesManager();
|
||||
|
||||
if (permissionHook != null) {
|
||||
sm.unregister(Permission.class, permissionHook);
|
||||
permissionHook.getScheduler().cancelTask();
|
||||
permissionHook.getExecutor().close();
|
||||
permissionHook = null;
|
||||
}
|
||||
|
||||
if (chatHook != null) {
|
||||
sm.unregister(Chat.class, chatHook);
|
||||
chatHook = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the Vault classes are registered.
|
||||
*
|
||||
* @return true if hooked
|
||||
*/
|
||||
public boolean isHooked() {
|
||||
return permissionHook != null && chatHook != null;
|
||||
}
|
@ -45,37 +45,52 @@ import me.lucko.luckperms.common.utils.ExtractedContexts;
|
||||
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* LuckPerms Vault Permission implementation
|
||||
* Most lookups are cached.
|
||||
* An implementation of the Vault {@link Permission} API using LuckPerms.
|
||||
*
|
||||
* Methods which change the state of data objects are likely to return immediately.
|
||||
*
|
||||
* LuckPerms is a multithreaded permissions plugin, and some actions require considerable
|
||||
* time to execute. (database queries, re-population of caches, etc) In these cases, the
|
||||
* methods will return immediately and the change will be executed asynchronously.
|
||||
*
|
||||
* Users of the Vault API expect these methods to be "main thread friendly", so unfortunately,
|
||||
* we have to favour so called "performance" for consistency. The Vault API really wasn't designed
|
||||
* with database backed permission plugins in mind. :(
|
||||
*
|
||||
* The methods which query offline players will explicitly FAIL if the corresponding player is not online.
|
||||
* We cannot risk blocking the main thread to load in their data. Again, this is due to crap Vault
|
||||
* design. There is nothing I can do about it.
|
||||
*/
|
||||
@Getter
|
||||
public class VaultPermissionHook extends Permission {
|
||||
|
||||
// the plugin instance
|
||||
private LPBukkitPlugin plugin;
|
||||
private VaultScheduler scheduler;
|
||||
|
||||
private final String name = "LuckPerms";
|
||||
|
||||
private Function<String, String> worldCorrectionFunction = s -> isIgnoreWorld() ? null : s;
|
||||
// an executor for Vault modifications.
|
||||
private VaultExecutor executor;
|
||||
|
||||
public VaultPermissionHook(LPBukkitPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.scheduler = new VaultScheduler(plugin);
|
||||
super.plugin = plugin;
|
||||
super.plugin = JavaPlugin.getProvidingPlugin(Permission.class);
|
||||
this.executor = new VaultExecutor(plugin);
|
||||
}
|
||||
|
||||
public void log(String s) {
|
||||
if (plugin.getConfiguration().get(ConfigKeys.VAULT_DEBUG)) {
|
||||
plugin.getLog().info("[VAULT] " + s);
|
||||
}
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LuckPerms";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -88,149 +103,122 @@ public class VaultPermissionHook extends Permission {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic method to add a permission to a holder
|
||||
*
|
||||
* @param world the world to add in
|
||||
* @param holder the holder to add the permission to
|
||||
* @param permission the permission to add
|
||||
*/
|
||||
private CompletableFuture<Void> add(String world, PermissionHolder holder, String permission) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
DataMutateResult result;
|
||||
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
|
||||
result = holder.setPermission(NodeFactory.make(permission, true, getServer(), world));
|
||||
} else {
|
||||
result = holder.setPermission(NodeFactory.make(permission, true, getServer()));
|
||||
}
|
||||
|
||||
if (result.asBoolean()) {
|
||||
save(holder);
|
||||
}
|
||||
}, scheduler);
|
||||
@Override
|
||||
public boolean hasGroupSupport() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic method to remove a permission from a holder
|
||||
*
|
||||
* @param world the world to remove in
|
||||
* @param holder the holder to remove the permission from
|
||||
* @param permission the permission to remove
|
||||
*/
|
||||
private CompletableFuture<Void> remove(String world, PermissionHolder holder, String permission) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
DataMutateResult result;
|
||||
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
|
||||
result = holder.unsetPermission(NodeFactory.make(permission, getServer(), world));
|
||||
} else {
|
||||
result = holder.unsetPermission(NodeFactory.make(permission, getServer()));
|
||||
}
|
||||
|
||||
if (result.asBoolean()) {
|
||||
save(holder);
|
||||
}
|
||||
}, scheduler);
|
||||
@Override
|
||||
public String[] getGroups() {
|
||||
return plugin.getGroupManager().getAll().keySet().toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to asynchronously save a user or group
|
||||
*
|
||||
* @param holder the holder instance
|
||||
*/
|
||||
public void save(PermissionHolder holder) {
|
||||
if (holder instanceof User) {
|
||||
User u = (User) holder;
|
||||
plugin.getStorage().saveUser(u).thenRunAsync(() -> u.getRefreshBuffer().request(), plugin.getScheduler().async());
|
||||
}
|
||||
if (holder instanceof Group) {
|
||||
Group g = (Group) holder;
|
||||
plugin.getStorage().saveGroup(g).thenRunAsync(() -> plugin.getUpdateTaskBuffer().request(), plugin.getScheduler().async());
|
||||
}
|
||||
@Override
|
||||
public boolean has(@NonNull CommandSender sender, @NonNull String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
public Contexts createContextForWorldSet(String world) {
|
||||
MutableContextSet context = MutableContextSet.create();
|
||||
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
|
||||
context.add("world", world.toLowerCase());
|
||||
}
|
||||
context.add("server", getServer());
|
||||
return new Contexts(context, isIncludeGlobal(), true, true, true, true, false);
|
||||
}
|
||||
|
||||
public Contexts createContextForWorldLookup(String world) {
|
||||
MutableContextSet context = MutableContextSet.create();
|
||||
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
|
||||
context.add("world", world.toLowerCase());
|
||||
}
|
||||
context.add("server", getServer());
|
||||
context.addAll(plugin.getConfiguration().getContextsFile().getStaticContexts());
|
||||
return new Contexts(context, isIncludeGlobal(), true, true, true, true, false);
|
||||
}
|
||||
|
||||
public Contexts createContextForWorldLookup(Player player, String world) {
|
||||
MutableContextSet context = MutableContextSet.create();
|
||||
|
||||
// use player context
|
||||
if (player != null) {
|
||||
ImmutableContextSet applicableContext = plugin.getContextManager().getApplicableContext(player);
|
||||
context.addAll(applicableContext);
|
||||
} else {
|
||||
// at least given them the static context defined for this instance
|
||||
context.addAll(plugin.getConfiguration().getContextsFile().getStaticContexts());
|
||||
}
|
||||
|
||||
// worlds & servers get set depending on the config setting
|
||||
context.removeAll("world");
|
||||
context.removeAll("server");
|
||||
|
||||
// add the vault settings
|
||||
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
|
||||
context.add("world", world.toLowerCase());
|
||||
}
|
||||
context.add("server", getServer());
|
||||
|
||||
return new Contexts(context, isIncludeGlobal(), true, true, true, true, false);
|
||||
@Override
|
||||
public boolean has(@NonNull Player player, @NonNull String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerHas(String world, @NonNull String player, @NonNull String permission) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
return playerHas(world, Bukkit.getPlayerExact(player), permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerHas(String world, @NonNull OfflinePlayer player, @NonNull String permission) {
|
||||
return playerHas(world, player.getPlayer(), permission);
|
||||
}
|
||||
|
||||
private boolean playerHas(String world, Player player, String permission) {
|
||||
world = correctWorld(world);
|
||||
log("Checking if player " + player + " has permission: " + permission + " on world " + world + ", server " + getServer());
|
||||
|
||||
User user = plugin.getUserManager().getByUsername(player);
|
||||
if (user == null) return false;
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Effectively fallback to the standard Bukkit #hasPermission check.
|
||||
return user.getUserData().getPermissionData(createContextForWorldLookup(plugin.getPlayer(user), world)).getPermissionValue(permission).asBoolean();
|
||||
return user.getUserData().getPermissionData(createContextForWorldLookup(player, world)).getPermissionValue(permission).asBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerAdd(String world, @NonNull String player, @NonNull String permission) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
return playerAdd(world, Bukkit.getPlayerExact(player), permission);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean playerAdd(World world, @NonNull String player, @NonNull String permission) {
|
||||
return playerAdd(world == null ? null : world.getName(), Bukkit.getPlayerExact(player), permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerAdd(String world, @NonNull OfflinePlayer player, @NonNull String permission) {
|
||||
return playerAdd(world, player.getPlayer(), permission);
|
||||
}
|
||||
|
||||
private boolean playerAdd(String world, Player player, String permission) {
|
||||
world = correctWorld(world);
|
||||
log("Adding permission to player " + player + ": '" + permission + "' on world " + world + ", server " + getServer());
|
||||
|
||||
final User user = plugin.getUserManager().getByUsername(player);
|
||||
if (user == null) return false;
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
add(world, user, permission);
|
||||
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
holderAddPermission(user, permission, world);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerRemove(String world, @NonNull String player, @NonNull String permission) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
return playerRemove(world, Bukkit.getPlayerExact(player), permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerRemove(String world, @NonNull OfflinePlayer player, @NonNull String permission) {
|
||||
return playerRemove(world, player.getPlayer(), permission);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean playerRemove(World world, @NonNull String player, @NonNull String permission) {
|
||||
return playerRemove(world == null ? null : world.getName(), Bukkit.getPlayerExact(player), permission);
|
||||
}
|
||||
|
||||
private boolean playerRemove(String world, Player player, String permission) {
|
||||
world = correctWorld(world);
|
||||
log("Removing permission from player " + player + ": '" + permission + "' on world " + world + ", server " + getServer());
|
||||
|
||||
final User user = plugin.getUserManager().getByUsername(player);
|
||||
if (user == null) return false;
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
remove(world, user, permission);
|
||||
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
holderRemovePermission(user, permission, world);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean groupHas(String world, @NonNull String groupName, @NonNull String permission) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
world = correctWorld(world);
|
||||
log("Checking if group " + groupName + " has permission: " + permission + " on world " + world + ", server " + getServer());
|
||||
|
||||
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
@ -243,105 +231,123 @@ public class VaultPermissionHook extends Permission {
|
||||
|
||||
@Override
|
||||
public boolean groupAdd(String world, @NonNull String groupName, @NonNull String permission) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
world = correctWorld(world);
|
||||
log("Adding permission to group " + groupName + ": '" + permission + "' on world " + world + ", server " + getServer());
|
||||
|
||||
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
if (group == null) return false;
|
||||
|
||||
add(world, group, permission);
|
||||
holderAddPermission(group, permission, world);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean groupRemove(String world, @NonNull String groupName, @NonNull String permission) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
world = correctWorld(world);
|
||||
log("Removing permission from group " + groupName + ": '" + permission + "' on world " + world + ", server " + getServer());
|
||||
|
||||
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
if (group == null) return false;
|
||||
|
||||
remove(world, group, permission);
|
||||
holderRemovePermission(group, permission, world);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerInGroup(String world, @NonNull String player, @NonNull String group) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
log("Checking if player " + player + " is in group: " + group + " on world " + world + ", server " + getServer());
|
||||
public boolean playerInGroup(String world, String player, @NonNull String group) {
|
||||
return playerHas(world, player, "group." + group);
|
||||
}
|
||||
|
||||
final User user = plugin.getUserManager().getByUsername(player);
|
||||
if (user == null) return false;
|
||||
|
||||
String w = world; // screw effectively final
|
||||
return user.getNodes().values().stream()
|
||||
.filter(Node::isGroupNode)
|
||||
.filter(n -> n.shouldApplyWithContext(createContextForWorldLookup(plugin.getPlayer(user), w).getContexts()))
|
||||
.map(Node::getGroupName)
|
||||
.anyMatch(s -> s.equalsIgnoreCase(group));
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean playerInGroup(World world, String player, @NonNull String group) {
|
||||
return playerHas(world, player, "group." + group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerAddGroup(String world, @NonNull String player, @NonNull String groupName) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
log("Adding player " + player + " to group: '" + groupName + "' on world " + world + ", server " + getServer());
|
||||
|
||||
final User user = plugin.getUserManager().getByUsername(player);
|
||||
if (user == null) return false;
|
||||
|
||||
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
if (group == null) return false;
|
||||
|
||||
String w = world;
|
||||
scheduler.execute(() -> {
|
||||
DataMutateResult result;
|
||||
if (w != null && !w.equals("") && !w.equalsIgnoreCase("global")) {
|
||||
result = user.setInheritGroup(group, ImmutableContextSet.of("server", getServer(), "world", w));
|
||||
} else {
|
||||
result = user.setInheritGroup(group, ImmutableContextSet.singleton("server", getServer()));
|
||||
}
|
||||
|
||||
if (result.asBoolean()) {
|
||||
save(user);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
public boolean playerInGroup(String world, OfflinePlayer player, @NonNull String group) {
|
||||
return playerHas(world, player, "group." + group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerRemoveGroup(String world, @NonNull String player, @NonNull String groupName) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
log("Removing player " + player + " from group: '" + groupName + "' on world " + world + ", server " + getServer());
|
||||
public boolean playerInGroup(Player player, @NonNull String group) {
|
||||
return playerHas(player, "group." + group);
|
||||
}
|
||||
|
||||
final User user = plugin.getUserManager().getByUsername(player);
|
||||
if (user == null) return false;
|
||||
private boolean checkGroupExists(String group) {
|
||||
return plugin.getGroupManager().isLoaded(group);
|
||||
}
|
||||
|
||||
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
if (group == null) return false;
|
||||
@Override
|
||||
public boolean playerAddGroup(String world, String player, @NonNull String group) {
|
||||
return checkGroupExists(group) && playerAdd(world, player, "group." + group);
|
||||
}
|
||||
|
||||
String w = world;
|
||||
scheduler.execute(() -> {
|
||||
DataMutateResult result;
|
||||
if (w != null && !w.equals("") && !w.equalsIgnoreCase("global")) {
|
||||
result = user.unsetInheritGroup(group, ImmutableContextSet.of("server", getServer(), "world", w));
|
||||
} else {
|
||||
result = user.unsetInheritGroup(group, ImmutableContextSet.singleton("server", getServer()));
|
||||
}
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean playerAddGroup(World world, String player, @NonNull String group) {
|
||||
return checkGroupExists(group) && playerAdd(world, player, "group." + group);
|
||||
}
|
||||
|
||||
if (result.asBoolean()) {
|
||||
save(user);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
@Override
|
||||
public boolean playerAddGroup(String world, OfflinePlayer player, @NonNull String group) {
|
||||
return checkGroupExists(group) && playerAdd(world, player, "group." + group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerAddGroup(Player player, @NonNull String group) {
|
||||
return checkGroupExists(group) && playerAdd(player, "group." + group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerRemoveGroup(String world, String player, @NonNull String group) {
|
||||
return checkGroupExists(group) && playerRemove(world, player, "group." + group);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean playerRemoveGroup(World world, String player, @NonNull String group) {
|
||||
return checkGroupExists(group) && playerRemove(world, player, "group." + group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerRemoveGroup(String world, OfflinePlayer player, @NonNull String group) {
|
||||
return checkGroupExists(group) && playerRemove(world, player, "group." + group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerRemoveGroup(Player player, @NonNull String group) {
|
||||
return checkGroupExists(group) && playerRemove(player, "group." + group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlayerGroups(String world, @NonNull String player) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
return getPlayerGroups(world, Bukkit.getPlayerExact(player));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public String[] getPlayerGroups(World world, @NonNull String player) {
|
||||
return getPlayerGroups(world == null ? null : world.getName(), Bukkit.getPlayerExact(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlayerGroups(String world, @NonNull OfflinePlayer player) {
|
||||
return getPlayerGroups(world, player.getPlayer());
|
||||
}
|
||||
|
||||
private String[] getPlayerGroups(String world, Player player) {
|
||||
world = correctWorld(world);
|
||||
log("Getting groups of player: " + player + ", on world " + world + ", server " + getServer());
|
||||
|
||||
User user = plugin.getUserManager().getByUsername(player);
|
||||
if (user == null) return new String[0];
|
||||
if (player == null) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
|
||||
if (user == null) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
String w = world; // screw effectively final
|
||||
return user.getNodes().values().stream()
|
||||
@ -353,9 +359,29 @@ public class VaultPermissionHook extends Permission {
|
||||
|
||||
@Override
|
||||
public String getPrimaryGroup(String world, @NonNull String player) {
|
||||
world = worldCorrectionFunction.apply(world);
|
||||
return getPrimaryGroup(world, Bukkit.getPlayerExact(player));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public String getPrimaryGroup(World world, @NonNull String player) {
|
||||
return getPrimaryGroup(world == null ? null : world.getName(), Bukkit.getPlayerExact(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrimaryGroup(String world, @NonNull OfflinePlayer player) {
|
||||
return getPrimaryGroup(world, player.getPlayer());
|
||||
}
|
||||
|
||||
private String getPrimaryGroup(String world, Player player) {
|
||||
world = correctWorld(world);
|
||||
log("Getting primary group of player: " + player);
|
||||
final User user = plugin.getUserManager().getByUsername(player);
|
||||
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
|
||||
|
||||
if (user == null) {
|
||||
return null;
|
||||
@ -428,16 +454,102 @@ public class VaultPermissionHook extends Permission {
|
||||
return plugin.getConfiguration().get(ConfigKeys.GROUP_NAME_REWRITES).getOrDefault(g, g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getGroups() {
|
||||
return plugin.getGroupManager().getAll().keySet().toArray(new String[0]);
|
||||
public void log(String s) {
|
||||
if (plugin.getConfiguration().get(ConfigKeys.VAULT_DEBUG)) {
|
||||
plugin.getLog().info("[VAULT] " + s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasGroupSupport() {
|
||||
return true;
|
||||
String correctWorld(String world) {
|
||||
return isIgnoreWorld() ? null : world;
|
||||
}
|
||||
|
||||
// utility methods for modifying the state of PermissionHolders
|
||||
|
||||
private void holderAddPermission(PermissionHolder holder, String permission, String world) {
|
||||
executor.execute(() -> {
|
||||
DataMutateResult result;
|
||||
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
|
||||
result = holder.setPermission(NodeFactory.make(permission, true, getServer(), world));
|
||||
} else {
|
||||
result = holder.setPermission(NodeFactory.make(permission, true, getServer()));
|
||||
}
|
||||
|
||||
if (result.asBoolean()) {
|
||||
holderSave(holder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void holderRemovePermission(PermissionHolder holder, String permission, String world) {
|
||||
executor.execute(() -> {
|
||||
DataMutateResult result;
|
||||
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
|
||||
result = holder.unsetPermission(NodeFactory.make(permission, getServer(), world));
|
||||
} else {
|
||||
result = holder.unsetPermission(NodeFactory.make(permission, getServer()));
|
||||
}
|
||||
|
||||
if (result.asBoolean()) {
|
||||
holderSave(holder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void holderSave(PermissionHolder holder) {
|
||||
if (holder instanceof User) {
|
||||
User u = (User) holder;
|
||||
plugin.getStorage().saveUser(u).thenRunAsync(() -> u.getRefreshBuffer().request(), plugin.getScheduler().async());
|
||||
}
|
||||
if (holder instanceof Group) {
|
||||
Group g = (Group) holder;
|
||||
plugin.getStorage().saveGroup(g).thenRunAsync(() -> plugin.getUpdateTaskBuffer().request(), plugin.getScheduler().async());
|
||||
}
|
||||
}
|
||||
|
||||
// helper methods to build Contexts instances for different world/server combinations
|
||||
|
||||
public Contexts createContextForWorldSet(String world) {
|
||||
MutableContextSet context = MutableContextSet.create();
|
||||
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
|
||||
context.add("world", world.toLowerCase());
|
||||
}
|
||||
context.add("server", getServer());
|
||||
return new Contexts(context, isIncludeGlobal(), true, true, true, true, false);
|
||||
}
|
||||
|
||||
public Contexts createContextForWorldLookup(String world) {
|
||||
MutableContextSet context = MutableContextSet.create();
|
||||
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
|
||||
context.add("world", world.toLowerCase());
|
||||
}
|
||||
context.add("server", getServer());
|
||||
context.addAll(plugin.getConfiguration().getContextsFile().getStaticContexts());
|
||||
return new Contexts(context, isIncludeGlobal(), true, true, true, true, false);
|
||||
}
|
||||
|
||||
public Contexts createContextForWorldLookup(@NonNull Player player, String world) {
|
||||
MutableContextSet context = MutableContextSet.create();
|
||||
|
||||
// use player context
|
||||
ImmutableContextSet applicableContext = plugin.getContextManager().getApplicableContext(player);
|
||||
context.addAll(applicableContext);
|
||||
|
||||
// worlds & servers get set depending on the config setting
|
||||
context.removeAll("world");
|
||||
context.removeAll("server");
|
||||
|
||||
// add the vault settings
|
||||
if (world != null && !world.isEmpty() && !world.equalsIgnoreCase("global")) {
|
||||
context.add("world", world.toLowerCase());
|
||||
}
|
||||
context.add("server", getServer());
|
||||
|
||||
return new Contexts(context, isIncludeGlobal(), true, true, true, true, false);
|
||||
}
|
||||
|
||||
// helper methods to just pull values from the config.
|
||||
|
||||
String getServer() {
|
||||
return plugin.getConfiguration().get(ConfigKeys.VAULT_SERVER);
|
||||
}
|
||||
|
@ -150,16 +150,7 @@ public class BungeeListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
Contexts contexts = new Contexts(
|
||||
plugin.getContextManager().getApplicableContext(player),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
false
|
||||
);
|
||||
|
||||
Contexts contexts = plugin.getContextManager().getApplicableContexts(player);
|
||||
Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission());
|
||||
if (result == Tristate.UNDEFINED && plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) {
|
||||
return; // just use the result provided by the proxy when the event was created
|
||||
@ -182,16 +173,7 @@ public class BungeeListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
Contexts contexts = new Contexts(
|
||||
plugin.getContextManager().getApplicableContext(player),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
false
|
||||
);
|
||||
|
||||
Contexts contexts = plugin.getContextManager().getApplicableContexts(player);
|
||||
Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission());
|
||||
if (result == Tristate.UNDEFINED && plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) {
|
||||
return; // just use the result provided by the proxy when the event was created
|
||||
@ -216,16 +198,7 @@ public class BungeeListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
Contexts contexts = new Contexts(
|
||||
set.makeImmutable(),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
false
|
||||
);
|
||||
|
||||
Contexts contexts = plugin.getContextManager().formContexts(e.getPlayer(), set.makeImmutable());
|
||||
user.getUserData().preCalculate(contexts);
|
||||
});
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.Logger;
|
||||
import me.lucko.luckperms.api.PlatformType;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.bungee.messaging.BungeeMessagingService;
|
||||
import me.lucko.luckperms.bungee.messaging.RedisBungeeMessagingService;
|
||||
import me.lucko.luckperms.bungee.util.RedisBungeeUtil;
|
||||
@ -231,13 +232,26 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
||||
calculatorFactory = new BungeeCalculatorFactory(this);
|
||||
cachedStateManager = new CachedStateManager(this);
|
||||
|
||||
contextManager = new ContextManager<>();
|
||||
contextManager = new ContextManager<ProxiedPlayer>() {
|
||||
@Override
|
||||
public Contexts formContexts(ProxiedPlayer player, ImmutableContextSet contextSet) {
|
||||
return new Contexts(
|
||||
contextSet,
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
false
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
BackendServerCalculator serverCalculator = new BackendServerCalculator(this);
|
||||
contextManager.registerCalculator(serverCalculator);
|
||||
|
||||
StaticCalculator<ProxiedPlayer> staticCalculator = new StaticCalculator<>(getConfiguration());
|
||||
contextManager.registerCalculator(staticCalculator);
|
||||
contextManager.registerStaticCalculator(staticCalculator);
|
||||
contextManager.registerCalculator(staticCalculator, true);
|
||||
|
||||
// register with the LP API
|
||||
apiProvider = new ApiProvider(this);
|
||||
@ -338,15 +352,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
return new Contexts(
|
||||
getContextManager().getApplicableContext(player),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
false
|
||||
);
|
||||
return contextManager.getApplicableContexts(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -384,19 +390,11 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
||||
|
||||
@Override
|
||||
public Set<Contexts> getPreProcessContexts(boolean op) {
|
||||
Set<ContextSet> c = new HashSet<>();
|
||||
Set<ImmutableContextSet> c = new HashSet<>();
|
||||
c.add(ContextSet.empty());
|
||||
c.add(ContextSet.singleton("server", getConfiguration().get(ConfigKeys.SERVER)));
|
||||
return c.stream()
|
||||
.map(set -> new Contexts(
|
||||
set,
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
false
|
||||
))
|
||||
.map(set -> contextManager.formContexts(null, set))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ package me.lucko.luckperms.common.contexts;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.github.benmanes.caffeine.cache.RemovalListener;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.context.ContextCalculator;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.api.context.MutableContextSet;
|
||||
@ -36,20 +38,31 @@ import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ContextManager<T> {
|
||||
public abstract class ContextManager<T> {
|
||||
|
||||
private final List<ContextCalculator<T>> calculators = new CopyOnWriteArrayList<>();
|
||||
private final List<ContextCalculator<?>> staticCalculators = new CopyOnWriteArrayList<>();
|
||||
|
||||
private final LoadingCache<T, ImmutableContextSet> cache = Caffeine.newBuilder()
|
||||
private final LoadingCache<T, ImmutableContextSet> activeContextCache = Caffeine.newBuilder()
|
||||
.weakKeys()
|
||||
.expireAfterWrite(50L, TimeUnit.MILLISECONDS)
|
||||
.removalListener((RemovalListener<T, ImmutableContextSet>) (t, contextSet, removalCause) -> invalidateContextsCache(t))
|
||||
.build(t -> calculateApplicableContext(t, MutableContextSet.create()).makeImmutable());
|
||||
|
||||
private final LoadingCache<T, Contexts> contextsCache = Caffeine.newBuilder()
|
||||
.weakKeys()
|
||||
.build(t -> formContexts(t, getApplicableContext(t)));
|
||||
|
||||
public ImmutableContextSet getApplicableContext(T subject) {
|
||||
return cache.get(subject);
|
||||
return activeContextCache.get(subject);
|
||||
}
|
||||
|
||||
public Contexts getApplicableContexts(T subject) {
|
||||
return contextsCache.get(subject);
|
||||
}
|
||||
|
||||
public abstract Contexts formContexts(T t, ImmutableContextSet contextSet);
|
||||
|
||||
private MutableContextSet calculateApplicableContext(T subject, MutableContextSet accumulator) {
|
||||
for (ContextCalculator<T> calculator : calculators) {
|
||||
try {
|
||||
@ -62,13 +75,21 @@ public class ContextManager<T> {
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
public void registerCalculator(ContextCalculator<T> calculator) {
|
||||
// calculators registered first should have priority (and be checked last.)
|
||||
calculators.add(0, calculator);
|
||||
private void invalidateContextsCache(T t) {
|
||||
contextsCache.invalidate(t);
|
||||
}
|
||||
|
||||
public void registerStaticCalculator(ContextCalculator<?> calculator) {
|
||||
staticCalculators.add(0, calculator);
|
||||
public void registerCalculator(ContextCalculator<T> calculator) {
|
||||
registerCalculator(calculator, false);
|
||||
}
|
||||
|
||||
public void registerCalculator(ContextCalculator<T> calculator, boolean isStatic) {
|
||||
// calculators registered first should have priority (and be checked last.)
|
||||
calculators.add(0, calculator);
|
||||
|
||||
if (isStatic) {
|
||||
staticCalculators.add(0, calculator);
|
||||
}
|
||||
}
|
||||
|
||||
public ImmutableContextSet getStaticContexts() {
|
||||
@ -80,7 +101,7 @@ public class ContextManager<T> {
|
||||
}
|
||||
|
||||
public void invalidateCache(T subject){
|
||||
cache.invalidate(subject);
|
||||
activeContextCache.invalidate(subject);
|
||||
}
|
||||
|
||||
public int getCalculatorsSize() {
|
||||
|
@ -68,7 +68,7 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
* The users data cache instance, if present.
|
||||
*/
|
||||
@Getter
|
||||
private final UserCache userData;
|
||||
private final UserCache userData = new UserCache(this);
|
||||
|
||||
@Getter
|
||||
private BufferedRequest<Void> refreshBuffer = new BufferedRequest<Void>(1000L, r -> getPlugin().doAsync(r)) {
|
||||
@ -87,7 +87,6 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
this.uuid = uuid;
|
||||
|
||||
this.primaryGroup = plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION).apply(this);
|
||||
this.userData = new UserCache(this);
|
||||
getPlugin().getApiProvider().getEventFactory().handleUserCacheLoad(this, userData);
|
||||
}
|
||||
|
||||
@ -97,7 +96,6 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
|
||||
setName(name, false);
|
||||
this.primaryGroup = plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION).apply(this);
|
||||
this.userData = new UserCache(this);
|
||||
getPlugin().getApiProvider().getEventFactory().handleUserCacheLoad(this, userData);
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ import com.google.inject.Inject;
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.LuckPermsApi;
|
||||
import me.lucko.luckperms.api.PlatformType;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.api.ApiHandler;
|
||||
import me.lucko.luckperms.common.api.ApiProvider;
|
||||
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
|
||||
@ -274,12 +275,25 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
||||
calculatorFactory = new SpongeCalculatorFactory(this);
|
||||
cachedStateManager = new CachedStateManager(this);
|
||||
|
||||
contextManager = new ContextManager<>();
|
||||
contextManager = new ContextManager<Subject>() {
|
||||
@Override
|
||||
public Contexts formContexts(Subject subject, ImmutableContextSet contextSet) {
|
||||
return new Contexts(
|
||||
contextSet,
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
false
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
contextManager.registerCalculator(new WorldCalculator(this));
|
||||
|
||||
StaticCalculator<Subject> staticCalculator = new StaticCalculator<>(getConfiguration());
|
||||
contextManager.registerCalculator(staticCalculator);
|
||||
contextManager.registerStaticCalculator(staticCalculator);
|
||||
contextManager.registerCalculator(staticCalculator, true);
|
||||
|
||||
// register the PermissionService with Sponge
|
||||
getLog().info("Registering PermissionService...");
|
||||
@ -424,15 +438,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
return new Contexts(
|
||||
getContextManager().getApplicableContext(player),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
false
|
||||
);
|
||||
return contextManager.getApplicableContexts(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -190,7 +190,7 @@ public class SpongeUser extends User {
|
||||
@Override
|
||||
public ImmutableContextSet getActiveContextSet() {
|
||||
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_ACTIVE_CONTEXTS)) {
|
||||
return plugin.getContextManager().getApplicableContext(this.sponge()).makeImmutable();
|
||||
return plugin.getContextManager().getApplicableContext(this.sponge());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.caching.UserCache;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.core.model.Group;
|
||||
import me.lucko.luckperms.common.core.model.User;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
@ -253,15 +252,7 @@ public class LuckPermsService implements LPPermissionService {
|
||||
|
||||
@Override
|
||||
public Contexts calculateContexts(ImmutableContextSet contextSet) {
|
||||
return new Contexts(
|
||||
contextSet,
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
false
|
||||
);
|
||||
return plugin.getContextManager().formContexts(null, contextSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user