Release 2.7
This commit is contained in:
@@ -46,7 +46,7 @@ class BukkitListener extends AbstractListener implements Listener {
|
||||
@EventHandler
|
||||
public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) {
|
||||
if (!plugin.getDatastore().isAcceptingLogins()) {
|
||||
// Datastore is disabled, prevent players from joining the server
|
||||
// The datastore is disabled, prevent players from joining the server
|
||||
e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.toString());
|
||||
return;
|
||||
}
|
||||
@@ -70,6 +70,11 @@ class BukkitListener extends AbstractListener implements Listener {
|
||||
PermissionAttachment attachment = player.addAttachment(plugin);
|
||||
Map<String, Boolean> newPermMap = new ConcurrentHashMap<>();
|
||||
try {
|
||||
/* Replace the standard LinkedHashMap in the attachment with a ConcurrentHashMap.
|
||||
This means that we can iterate over and change the permissions within our attachment asynchronously,
|
||||
without worrying about thread safety. The Bukkit side of things should still operate normally. Internal
|
||||
permission stuff should work the same. This is by far the most easy and efficient way to do things, without
|
||||
having to do tons of reflection. */
|
||||
BukkitUser.getPermissionsField().set(attachment, newPermMap);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
|
||||
@@ -25,6 +25,7 @@ package me.lucko.luckperms;
|
||||
import lombok.Getter;
|
||||
import me.lucko.luckperms.api.Logger;
|
||||
import me.lucko.luckperms.api.LuckPermsApi;
|
||||
import me.lucko.luckperms.api.PlatformType;
|
||||
import me.lucko.luckperms.api.implementation.ApiProvider;
|
||||
import me.lucko.luckperms.api.vault.VaultHook;
|
||||
import me.lucko.luckperms.commands.ConsecutiveExecutor;
|
||||
@@ -161,8 +162,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.BUKKIT;
|
||||
public PlatformType getType() {
|
||||
return PlatformType.BUKKIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,6 +32,7 @@ import me.lucko.luckperms.groups.Group;
|
||||
import me.lucko.luckperms.users.User;
|
||||
import net.milkbowl.vault.chat.Chat;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters;
|
||||
@@ -80,108 +81,30 @@ public class VaultChatHook extends Chat {
|
||||
node = escapeCharacters(node);
|
||||
value = escapeCharacters(value);
|
||||
|
||||
if (world == null || world.equals("")) {
|
||||
try {
|
||||
holder.setPermission("meta." + node + "." + value, true);
|
||||
} catch (ObjectAlreadyHasException ignored) {}
|
||||
|
||||
} else {
|
||||
try {
|
||||
holder.setPermission("meta." + node + "." + value, true, "global", world);
|
||||
} catch (ObjectAlreadyHasException ignored) {}
|
||||
Iterator<Node> nodes = holder.getNodes().iterator();
|
||||
while (nodes.hasNext()) {
|
||||
Node n = nodes.next();
|
||||
if (n.isMeta() && n.getMeta().getKey().equals(node)) {
|
||||
nodes.remove();
|
||||
}
|
||||
}
|
||||
|
||||
Node.Builder metaNode = new me.lucko.luckperms.utils.Node.Builder("meta." + node + "." + value).setValue(true);
|
||||
if (!perms.getServer().equalsIgnoreCase("global")) {
|
||||
metaNode.setServer(perms.getServer());
|
||||
}
|
||||
if (world != null && !world.equals("")) {
|
||||
metaNode.setServer(perms.getServer()).setWorld(world);
|
||||
}
|
||||
|
||||
try {
|
||||
holder.setPermission(metaNode.build());
|
||||
} catch (ObjectAlreadyHasException ignored) {}
|
||||
|
||||
perms.objectSave(holder);
|
||||
}
|
||||
|
||||
private static int getMeta(PermissionHolder holder, String world, String node, int defaultValue) {
|
||||
if (holder == null) return defaultValue;
|
||||
if (node.equals("")) return defaultValue;
|
||||
node = escapeCharacters(node);
|
||||
|
||||
for (Node n : holder.getPermissions()) {
|
||||
if (!n.isMeta()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!n.shouldApplyOnWorld(world, true, false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map.Entry<String, String> meta = n.getMeta();
|
||||
if (meta.getKey().equalsIgnoreCase(node)) {
|
||||
|
||||
try {
|
||||
return Integer.parseInt(unescapeCharacters(meta.getValue()));
|
||||
} catch (Throwable t) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private static double getMeta(PermissionHolder holder, String world, String node, double defaultValue) {
|
||||
if (holder == null) return defaultValue;
|
||||
if (node.equals("")) return defaultValue;
|
||||
node = escapeCharacters(node);
|
||||
|
||||
for (Node n : holder.getPermissions()) {
|
||||
if (!n.isMeta()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!n.shouldApplyOnWorld(world, true, false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map.Entry<String, String> meta = n.getMeta();
|
||||
if (meta.getKey().equalsIgnoreCase(node)) {
|
||||
|
||||
try {
|
||||
return Double.parseDouble(unescapeCharacters(meta.getValue()));
|
||||
} catch (Throwable t) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private static boolean getMeta(PermissionHolder holder, String world, String node, boolean defaultValue) {
|
||||
if (holder == null) return defaultValue;
|
||||
if (node.equals("")) return defaultValue;
|
||||
node = escapeCharacters(node);
|
||||
|
||||
for (Node n : holder.getPermissions()) {
|
||||
if (!n.isMeta()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!n.shouldApplyOnWorld(world, true, false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map.Entry<String, String> meta = n.getMeta();
|
||||
if (meta.getKey().equalsIgnoreCase(node)) {
|
||||
|
||||
try {
|
||||
return Boolean.parseBoolean(unescapeCharacters(meta.getValue()));
|
||||
} catch (Throwable t) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private static String getMeta(PermissionHolder holder, String world, String node, String defaultValue) {
|
||||
private String getMeta(PermissionHolder holder, String world, String node, String defaultValue) {
|
||||
if (holder == null) return defaultValue;
|
||||
if (node.equals("")) return defaultValue;
|
||||
node = escapeCharacters(node);
|
||||
@@ -195,29 +118,50 @@ public class VaultChatHook extends Chat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!n.shouldApplyOnWorld(world, true, false)) {
|
||||
if (!perms.getServer().equalsIgnoreCase("global")) {
|
||||
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map.Entry<String, String> meta = n.getMeta();
|
||||
if (meta.getKey().equalsIgnoreCase(node)) {
|
||||
|
||||
try {
|
||||
return unescapeCharacters(meta.getValue());
|
||||
} catch (Throwable t) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return unescapeCharacters(meta.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private static String getChatMeta(boolean prefix, PermissionHolder holder, String world) {
|
||||
private void setChatMeta(boolean prefix, PermissionHolder holder, String value, String world) {
|
||||
if (holder == null) return;
|
||||
if (value.equals("")) return;
|
||||
|
||||
Node.Builder node = new me.lucko.luckperms.utils.Node.Builder(prefix ? "prefix" : "suffix" + ".1000." + escapeCharacters(value));
|
||||
node.setValue(true);
|
||||
if (!perms.getServer().equalsIgnoreCase("global")) {
|
||||
node.setServer(perms.getServer());
|
||||
}
|
||||
|
||||
if (world != null && !world.equals("")) {
|
||||
node.setServer(perms.getServer()).setWorld(world);
|
||||
}
|
||||
|
||||
try {
|
||||
holder.setPermission(node.build());
|
||||
} catch (ObjectAlreadyHasException ignored) {}
|
||||
|
||||
perms.objectSave(holder);
|
||||
}
|
||||
|
||||
private String getChatMeta(boolean prefix, PermissionHolder holder, String world) {
|
||||
if (holder == null) return "";
|
||||
|
||||
int priority = -1000;
|
||||
int priority = Integer.MIN_VALUE;
|
||||
String meta = null;
|
||||
|
||||
for (Node n : holder.getAllNodes(null)) {
|
||||
@@ -225,30 +169,24 @@ public class VaultChatHook extends Chat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!n.shouldApplyOnWorld(world, true, false)) {
|
||||
if (!perms.getServer().equalsIgnoreCase("global")) {
|
||||
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
if (!n.isPrefix()) {
|
||||
continue;
|
||||
}
|
||||
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map.Entry<Integer, String> prefixValue = n.getPrefix();
|
||||
if (prefixValue.getKey() > priority) {
|
||||
meta = prefixValue.getValue();
|
||||
priority = prefixValue.getKey();
|
||||
}
|
||||
} else {
|
||||
if (!n.isSuffix()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map.Entry<Integer, String> suffixValue = n.getSuffix();
|
||||
if (suffixValue.getKey() > priority) {
|
||||
meta = suffixValue.getValue();
|
||||
priority = suffixValue.getKey();
|
||||
}
|
||||
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
|
||||
if (value.getKey() > priority) {
|
||||
meta = value.getValue();
|
||||
priority = value.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,15 +200,7 @@ public class VaultChatHook extends Chat {
|
||||
|
||||
public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) {
|
||||
final User user = plugin.getUserManager().get(player);
|
||||
if (user == null) return;
|
||||
|
||||
if (prefix.equals("")) return;
|
||||
|
||||
try {
|
||||
user.setPermission("prefix.1000." + escapeCharacters(prefix), true);
|
||||
} catch (ObjectAlreadyHasException ignored) {}
|
||||
|
||||
perms.objectSave(user);
|
||||
setChatMeta(true, user, prefix, world);
|
||||
}
|
||||
|
||||
public String getPlayerSuffix(String world, @NonNull String player) {
|
||||
@@ -280,15 +210,7 @@ public class VaultChatHook extends Chat {
|
||||
|
||||
public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) {
|
||||
final User user = plugin.getUserManager().get(player);
|
||||
if (user == null) return;
|
||||
|
||||
if (suffix.equals("")) return;
|
||||
|
||||
try {
|
||||
user.setPermission("suffix.1000." + escapeCharacters(suffix), true);
|
||||
} catch (ObjectAlreadyHasException ignored) {}
|
||||
|
||||
perms.objectSave(user);
|
||||
setChatMeta(false, user, suffix, world);
|
||||
}
|
||||
|
||||
public String getGroupPrefix(String world, @NonNull String group) {
|
||||
@@ -298,15 +220,7 @@ public class VaultChatHook extends Chat {
|
||||
|
||||
public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) {
|
||||
final Group g = plugin.getGroupManager().get(group);
|
||||
if (g == null) return;
|
||||
|
||||
if (prefix.equals("")) return;
|
||||
|
||||
try {
|
||||
g.setPermission("prefix.1000." + escapeCharacters(prefix), true);
|
||||
} catch (ObjectAlreadyHasException ignored) {}
|
||||
|
||||
perms.objectSave(g);
|
||||
setChatMeta(true, g, prefix, world);
|
||||
}
|
||||
|
||||
public String getGroupSuffix(String world, @NonNull String group) {
|
||||
@@ -316,20 +230,16 @@ public class VaultChatHook extends Chat {
|
||||
|
||||
public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) {
|
||||
final Group g = plugin.getGroupManager().get(group);
|
||||
if (g == null) return;
|
||||
|
||||
if (suffix.equals("")) return;
|
||||
|
||||
try {
|
||||
g.setPermission("suffix.1000." + escapeCharacters(suffix), true);
|
||||
} catch (ObjectAlreadyHasException ignored) {}
|
||||
|
||||
perms.objectSave(g);
|
||||
setChatMeta(false, g, suffix, world);
|
||||
}
|
||||
|
||||
public int getPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int defaultValue) {
|
||||
final User user = plugin.getUserManager().get(player);
|
||||
return getMeta(user, world, node, defaultValue);
|
||||
try {
|
||||
return Integer.parseInt(getMeta(user, world, node, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int value) {
|
||||
@@ -339,7 +249,11 @@ public class VaultChatHook extends Chat {
|
||||
|
||||
public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) {
|
||||
final Group g = plugin.getGroupManager().get(group);
|
||||
return getMeta(g, world, node, defaultValue);
|
||||
try {
|
||||
return Integer.parseInt(getMeta(g, world, node, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void setGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int value) {
|
||||
@@ -349,7 +263,11 @@ public class VaultChatHook extends Chat {
|
||||
|
||||
public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) {
|
||||
final User user = plugin.getUserManager().get(player);
|
||||
return getMeta(user, world, node, defaultValue);
|
||||
try {
|
||||
return Double.parseDouble(getMeta(user, world, node, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double value) {
|
||||
@@ -359,7 +277,11 @@ public class VaultChatHook extends Chat {
|
||||
|
||||
public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) {
|
||||
final Group g = plugin.getGroupManager().get(group);
|
||||
return getMeta(g, world, node, defaultValue);
|
||||
try {
|
||||
return Double.parseDouble(getMeta(g, world, node, String.valueOf(defaultValue)));
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void setGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double value) {
|
||||
@@ -369,7 +291,11 @@ public class VaultChatHook extends Chat {
|
||||
|
||||
public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) {
|
||||
final User user = plugin.getUserManager().get(player);
|
||||
return getMeta(user, world, node, defaultValue);
|
||||
String s = getMeta(user, world, node, String.valueOf(defaultValue));
|
||||
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(s);
|
||||
}
|
||||
|
||||
public void setPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean value) {
|
||||
@@ -379,7 +305,11 @@ public class VaultChatHook extends Chat {
|
||||
|
||||
public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) {
|
||||
final Group g = plugin.getGroupManager().get(group);
|
||||
return getMeta(g, world, node, defaultValue);
|
||||
String s = getMeta(g, world, node, String.valueOf(defaultValue));
|
||||
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(s);
|
||||
}
|
||||
|
||||
public void setGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean value) {
|
||||
|
||||
@@ -39,6 +39,8 @@ public class VaultHook {
|
||||
permissionHook = new VaultPermissionHook();
|
||||
}
|
||||
permissionHook.setPlugin(plugin);
|
||||
permissionHook.setServer(plugin.getConfiguration().getVaultServer());
|
||||
permissionHook.setIncludeGlobal(plugin.getConfiguration().getVaultIncludeGlobal());
|
||||
|
||||
if (chatHook == null) {
|
||||
chatHook = new VaultChatHook(permissionHook);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
package me.lucko.luckperms.api.vault;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import me.lucko.luckperms.LPBukkitPlugin;
|
||||
@@ -38,6 +39,14 @@ public class VaultPermissionHook extends Permission {
|
||||
@Setter
|
||||
private LPBukkitPlugin plugin;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String server = "global";
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean includeGlobal = true;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LuckPerms";
|
||||
@@ -57,9 +66,9 @@ public class VaultPermissionHook extends Permission {
|
||||
if (object == null) return false;
|
||||
|
||||
if (world != null && !world.equals("")) {
|
||||
return object.hasPermission(permission, true, "global", world);
|
||||
return object.hasPermission(permission, true, server, world);
|
||||
} else {
|
||||
return object.hasPermission(permission, true);
|
||||
return object.hasPermission(permission, true, server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +77,9 @@ public class VaultPermissionHook extends Permission {
|
||||
|
||||
try {
|
||||
if (world != null && !world.equals("")) {
|
||||
object.setPermission(permission, true, "global", world);
|
||||
object.setPermission(permission, true, server, world);
|
||||
} else {
|
||||
object.setPermission(permission, true);
|
||||
object.setPermission(permission, true, server);
|
||||
}
|
||||
} catch (ObjectAlreadyHasException ignored) {}
|
||||
|
||||
@@ -83,9 +92,9 @@ public class VaultPermissionHook extends Permission {
|
||||
|
||||
try {
|
||||
if (world != null && !world.equals("")) {
|
||||
object.unsetPermission(permission, "global", world);
|
||||
object.unsetPermission(permission, server, world);
|
||||
} else {
|
||||
object.unsetPermission(permission);
|
||||
object.unsetPermission(permission, server);
|
||||
}
|
||||
} catch (ObjectLacksException ignored) {}
|
||||
|
||||
@@ -147,7 +156,7 @@ public class VaultPermissionHook extends Permission {
|
||||
if (group1 == null) return false;
|
||||
|
||||
if (world != null && !world.equals("")) {
|
||||
return user.isInGroup(group1, "global", world);
|
||||
return user.isInGroup(group1, server, world);
|
||||
} else {
|
||||
return user.isInGroup(group1);
|
||||
}
|
||||
@@ -163,7 +172,7 @@ public class VaultPermissionHook extends Permission {
|
||||
|
||||
try {
|
||||
if (world != null && !world.equals("")) {
|
||||
user.addGroup(group, "global", world);
|
||||
user.addGroup(group, server, world);
|
||||
} else {
|
||||
user.addGroup(group);
|
||||
}
|
||||
@@ -182,7 +191,7 @@ public class VaultPermissionHook extends Permission {
|
||||
|
||||
try {
|
||||
if (world != null && !world.equals("")) {
|
||||
user.removeGroup(group, "global", world);
|
||||
user.removeGroup(group, server, world);
|
||||
} else {
|
||||
user.removeGroup(group);
|
||||
}
|
||||
@@ -195,7 +204,7 @@ public class VaultPermissionHook extends Permission {
|
||||
public String[] getPlayerGroups(String world, @NonNull String player) {
|
||||
final User user = plugin.getUserManager().get(player);
|
||||
return (user == null) ? new String[0] :
|
||||
world != null && !world.equals("") ? user.getGroups("global", world, true).toArray(new String[0]) :
|
||||
world != null && !world.equals("") ? user.getGroups(server, world, includeGlobal).toArray(new String[0]) :
|
||||
user.getGroupNames().toArray(new String[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ public class BukkitUser extends User {
|
||||
);
|
||||
|
||||
try {
|
||||
// The map in the LP PermissionAttachment is a ConcurrentHashMap. We can modify it's contents async.
|
||||
// The map in the LP PermissionAttachment is a ConcurrentHashMap. We can modify and iterate over its contents async.
|
||||
Map<String, Boolean> existing = attachment.getPermissions();
|
||||
|
||||
boolean different = false;
|
||||
|
||||
@@ -46,6 +46,13 @@ apply-shorthand: true
|
||||
# If the plugin should send log notifications to users whenever permissions are modified.
|
||||
log-notify: true
|
||||
|
||||
# The name of the server used within Vault operations. If you don't want Vault operations to be server specific, set this
|
||||
# to "global".
|
||||
vault-server: global
|
||||
|
||||
# If global permissions should be considered when retrieving meta or player groups
|
||||
vault-include-global: true
|
||||
|
||||
# Which storage method the plugin should use.
|
||||
# Currently supported: mysql, sqlite, h2, json, yaml, mongodb
|
||||
# Fill out connection info below if you're using MySQL or MongoDB
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
name: LuckPerms
|
||||
author: Luck
|
||||
version: ${release.version}.${git.closest.tag.commit.count}
|
||||
description: A permissions plugin
|
||||
author: Luck
|
||||
website: https://github.com/lucko/LuckPerms
|
||||
|
||||
main: me.lucko.luckperms.LPBukkitPlugin
|
||||
softdepend: [Vault, PermissionsEx, GroupManager, PowerfulPerms, zPermissions, bPermissions] # For migration
|
||||
description: A permissions plugin
|
||||
|
||||
commands:
|
||||
luckperms:
|
||||
description: Manage permissions
|
||||
aliases: [perms, permissions, lp, p, perm]
|
||||
|
||||
permissions:
|
||||
luckperms.*:
|
||||
description: Gives access to all LuckPerms commands
|
||||
|
||||
Reference in New Issue
Block a user