Compare commits

...
44 Commits
Author SHA1 Message Date
Luck ea07f05097 Release 2.8 2016-09-07 20:53:33 +01:00
Luck d84767af68 Fix blank users not receiving group permission updates 2016-09-06 22:28:37 +01:00
Luck aab73a8d81 Add more shorthand nodes 2016-09-06 22:27:46 +01:00
Luck 995fc2ccd8 Bump internal release version and add missing javadoc 2016-09-05 20:06:41 +01:00
Luck 60bcd5f643 Release 2.7 2016-09-05 20:00:01 +01:00
Luck ba04fb320b Remove unnecessary reflection 2016-09-05 16:44:59 +01:00
Luck 770b7dc282 Fix permissions not being removed on Sponge 2016-09-04 17:59:01 +01:00
Luck ed85eee36c Fix inherited permissions not applying on Sponge 2016-09-04 16:25:10 +01:00
Luck 23df8a2df8 Some small fixes 2016-09-04 16:24:36 +01:00
Luck 36cd388607 Slight re-wording 2016-09-03 21:34:35 +01:00
Luck 4d48ef1939 Add comment about Paper's "bungee-online-mode" option. Resolves #6 2016-09-03 21:30:37 +01:00
Luck 7970e2a1c6 Add bPermissions migration 2016-09-03 18:15:52 +01:00
Luck cee96d7daa make SenderFactory nicer & maybe faster 2016-09-03 00:14:40 +01:00
Luck 861c1ddd69 remove #childInit 2016-09-02 19:19:22 +01:00
Luck 5dec5a54cb Add YAML storage support 2016-09-02 19:15:31 +01:00
Luck 5c760aabd4 Fire PermissionRefreshEvent after the nodes have actually updated 2016-09-01 13:00:22 +01:00
Luck 1a2fa5d8c3 Fix log notify 2016-08-31 21:02:06 +01:00
Luck ef0c53da82 fix formatting 2016-08-31 09:38:47 +01:00
Luck 3fd62a80e2 Add support for split storage 2016-08-31 09:30:19 +01:00
Luck 5c19dd8b15 .-. 2016-08-30 22:38:08 +01:00
Luck 1fc88e147f Make output of listnodes nicer 2016-08-30 18:04:30 +01:00
Luck 9b4bbe0500 Make log notifications toggleable 2016-08-30 16:02:01 +01:00
Luck b37fdd3619 Add user to group instead of failing in setprimarygroup command 2016-08-30 13:23:48 +01:00
Luck ab00c41452 Fix NPE, and properly send possible permissions in BukkitUser 2016-08-30 13:06:36 +01:00
Luck c15727377b Apply single '*' wildcard 2016-08-30 12:49:46 +01:00
Luck 1e6f32bdc4 Add extra disallowed character 2016-08-30 11:54:18 +01:00
Luck 42e69c98a7 Change name limitation to be more flexible 2016-08-30 11:52:32 +01:00
Luck a217727bb4 Match negative priorities and fix PEX weight migration 2016-08-30 11:04:34 +01:00
Luck ba10b1c06e Fix removal of temporary nodes 2016-08-30 10:22:55 +01:00
Luck 075ba33e8b Add prefix/suffix/meta commands 2016-08-30 10:22:25 +01:00
Luck 19dbc7d90a formatting pls 2016-08-30 07:47:52 +01:00
Boomclaw ade70bb7ba PEX migration carries weights of prefixes/suffixes of groups and individuals (#5)
* PEX migration carries weights of prefixes/suffixes of groups and individuals.

* Forgot to increase maxGroupWeight.
2016-08-29 22:45:19 +01:00
Luck 4b67913eee getGroup methods should not include inherited groups 2016-08-29 22:03:10 +01:00
Luck 75858c1555 Use new Node system in #getGroups 2016-08-29 21:38:47 +01:00
Luck 659baaf5fb Make #getPlayerGroups return global too 2016-08-29 21:35:11 +01:00
Luck 3c83c578eb Fix typo 2016-08-29 18:40:37 +01:00
Luck 3ddbf1da87 Migrate PEX primary groups 2016-08-29 17:57:47 +01:00
Luck 35cb5b81dc hopefully fix server stopped responding error with SkipListSet 2016-08-29 16:28:36 +01:00
Luck 02d9b996a7 woops 2016-08-29 15:51:19 +01:00
Luck d4cf6a5ad7 Remove subjects from cache when they leave the server 2016-08-29 13:32:21 +01:00
Luck 9fdb4fa25f Maybe fix sponge permissions not expiring 2016-08-29 13:02:56 +01:00
Luck d5bc1cdf5d Fix not clearing user's permissions, and issue with unsetting permissions 2016-08-29 12:53:05 +01:00
Luck f5995c0afa Fix usernames showing as "null" and NPE related to primaryGroup not copying correctly 2016-08-29 12:11:03 +01:00
Luck 2cc65160b4 Bump release version 2016-08-28 22:12:34 +01:00
110 changed files with 4736 additions and 1259 deletions

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -28,7 +28,7 @@ import java.util.Set;
import java.util.UUID;
/**
* Wrapper interface for the internal Datastore instance
* Interface for the internal Datastore instance
*/
@SuppressWarnings("unused")
public interface Datastore {
@@ -55,7 +55,7 @@ public interface Datastore {
Async async();
/**
* Gets the {@link Future} interface
* Gets the {@link Future} interface.
*
* All operations through this interface are called in a new, separate asynchronous thread, similar to {@link Async}.
* The only difference is that instead of providing a callback, a {@link java.util.concurrent.Future} is returned.
@@ -112,7 +112,7 @@ public interface Datastore {
* @param username the users username. (if you want to specify <code>null</code> here, just input "null" as a string.)
* @return if the operation was performed successfully
* @throws NullPointerException if uuid or username is null
* @since 1.6
* @since 2.6
*/
boolean loadUser(UUID uuid, String username);
@@ -128,14 +128,14 @@ public interface Datastore {
/**
* Removes users from the datastore who are "default". This is called every time the plugin loads.
* @return true if the operation completed successfully
* @since 1.6
* @since 2.6
*/
boolean cleanupUsers();
/**
* Gets a set all user's UUIDs who are "unique", and aren't just a member of the "default" group.
* @return a set of uuids, or null if the operation failed.
* @since 1.6
* @since 2.6
*/
Set<UUID> getUniqueUsers();
@@ -28,7 +28,7 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
/**
* Wrapper interface for internal Group instances
* Interface for internal Group instances
*/
@SuppressWarnings("unused")
public interface Group extends PermissionHolder {
@@ -25,6 +25,8 @@ package me.lucko.luckperms.api;
import me.lucko.luckperms.api.data.DatastoreConfiguration;
import me.lucko.luckperms.api.data.MySQLConfiguration;
import java.util.Map;
/**
* A wrapper interface for the internal LuckPerms configuration, providing read only access.
*/
@@ -43,14 +45,14 @@ public interface LPConfiguration {
/**
* @return the default group, in a node representation
* @deprecated as of 1.6, the default group is always "default"
* @deprecated as of 2.6, the default group is always "default"
*/
@Deprecated
String getDefaultGroupNode();
/**
* @return the name of the default group
* @deprecated as of 1.6, the default group is always "default"
* @deprecated as of 2.6, the default group is always "default"
*/
@Deprecated
String getDefaultGroupName();
@@ -80,6 +82,36 @@ public interface LPConfiguration {
*/
boolean getApplyShorthand();
/**
* @return if LuckPerms will send notifications to users when permissions are modified
* @since 2.7
*/
boolean getLogNotify();
/**
* @return true if the vanilla op system is enabled
* @since 2.8
*/
boolean getEnableOps();
/**
* @return true if opped players are allowed to use LuckPerms commands
* @since 2.8
*/
boolean getCommandsAllowOp();
/**
* @return the name of the server used within Vault operations
* @since 2.7
*/
String getVaultServer();
/**
* @return true if global permissions should be considered when retrieving meta or player groups
* @since 2.7
*/
boolean getVaultIncludeGlobal();
/**
* @return the database values set in the configuration
* @deprecated use {@link #getDatastoreConfig()}
@@ -98,4 +130,17 @@ public interface LPConfiguration {
*/
String getStorageMethod();
/**
* @return true if split storage is enabled
* @since 2.7
*/
boolean getSplitStorage();
/**
* @return a map of split storage options, where the key is the storage section, and the value is the storage method.
* For example: key = user, value = json
* @since 2.7
*/
Map<String, String> getSplitStorageOptions();
}
@@ -27,6 +27,7 @@ import java.util.UUID;
/**
* Represents a single entry in a log
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public class LogEntry implements Comparable<LogEntry> {
public static LogEntryBuilder builder() {
return new LogEntryBuilder();
@@ -23,7 +23,7 @@
package me.lucko.luckperms.api;
/**
* A wrapper class for platform logger instances.
* A wrapper interface for platform logger instances.
*
* <p> Bukkit/Bungee both use java.util.logging, and Sponge uses org.slf4j. This class wraps those classes so the commons
* module can access a logger.
@@ -41,7 +41,7 @@ public interface LuckPermsApi {
/**
* @return the version of the API running on the platform
* @since 1.6
* @since 2.6
*/
double getApiVersion();
@@ -50,6 +50,12 @@ public interface LuckPermsApi {
*/
String getVersion();
/**
* @return the platform LuckPerms is running on
* @since 2.7
*/
PlatformType getPlatformType();
/**
* Registers a listener to be sent LuckPerms events
* @param listener the listener instance
@@ -138,7 +144,7 @@ public interface LuckPermsApi {
* Unload a user from the internal storage, if they're not currently online.
* @param user the user to unload
* @throws NullPointerException if the user is null
* @since 1.6
* @since 2.6
*/
void cleanupUser(User user);
@@ -208,7 +214,7 @@ public interface LuckPermsApi {
* @return a {@link Node.Builder} instance
* @throws IllegalArgumentException if the permission is invalid
* @throws NullPointerException if the permission is null
* @since 1.6
* @since 2.6
*/
Node.Builder buildNode(String permission) throws IllegalArgumentException;
@@ -0,0 +1,316 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
import me.lucko.luckperms.LuckPerms;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A collection of utilities to help retrieve meta values for {@link PermissionHolder}s
* @since 2.7
*/
public class MetaUtils {
private MetaUtils(){}
/**
* Escapes special characters used within LuckPerms, so the string can be saved without issues
* @param s the string to escape
* @return an escaped string
* @throws NullPointerException if the string is null
*/
public static String escapeCharacters(String s) {
if (s == null) {
throw new NullPointerException();
}
s = s.replace(".", "{SEP}");
s = s.replace("/", "{FSEP}");
s = s.replace("$", "{DSEP}");
return s;
}
/**
* Unescapes special characters used within LuckPerms, the inverse of {@link #escapeCharacters(String)}
* @param s the string to unescape
* @return an unescaped string
* @throws NullPointerException if the string is null
*/
public static String unescapeCharacters(String s) {
if (s == null) {
throw new NullPointerException();
}
s = s.replace("{SEP}", ".");
s = s.replace("{FSEP}", "/");
s = s.replace("{DSEP}", "$");
return s;
}
/**
* Sets a meta value on a holder
* @param holder the holder to apply the meta node to
* @param server the server to apply the meta on, can be null
* @param world the world to apply the meta on, can be null
* @param node the meta node
* @param value the meta value
* @throws NullPointerException if the holder, node or value is null
* @throws IllegalArgumentException if the node or value is empty
*/
public static void setMeta(PermissionHolder holder, String server, String world, String node, String value) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (node == null) {
throw new NullPointerException("node");
}
if (value == null) {
throw new NullPointerException("value");
}
if (node.equals("")) {
throw new IllegalArgumentException("node is empty");
}
if (value.equals("")) {
throw new IllegalArgumentException("value is empty");
}
if (server == null || server.equals("")) {
server = "global";
}
node = escapeCharacters(node);
value = escapeCharacters(value);
Set<Node> toRemove = new HashSet<>();
for (Node n : holder.getEnduringPermissions()) {
if (n.isMeta() && n.getMeta().getKey().equals(node)) {
toRemove.add(n);
}
}
for (Node n : toRemove) {
try {
holder.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
}
Node.Builder metaNode = LuckPerms.getApi().buildNode("meta." + node + "." + value).setValue(true);
if (!server.equalsIgnoreCase("global")) {
metaNode.setServer(server);
}
if (world != null && !world.equals("")) {
metaNode.setServer(server).setWorld(world);
}
try {
holder.setPermission(metaNode.build());
} catch (ObjectAlreadyHasException ignored) {}
}
/**
* Gets a meta value for the holder
* @param holder the holder to get the meta from
* @param server the server to retrieve the meta on, can be null
* @param world the world to retrieve the meta on, can be null
* @param node the node to get
* @param defaultValue the default value to return if the node is not set
* @param includeGlobal if global nodes should be considered when retrieving the meta
* @return a meta string, or the default value if the user does not have the meta node
* @throws NullPointerException if the holder or node is null
* @throws IllegalArgumentException if the node is empty
*/
public static String getMeta(PermissionHolder holder, String server, String world, String node, String defaultValue, boolean includeGlobal) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (server == null || server.equals("")) {
server = "global";
}
if (node == null) {
throw new NullPointerException("node");
}
if (node.equals("")) {
throw new IllegalArgumentException("node is empty");
}
node = escapeCharacters(node);
for (Node n : holder.getPermissions()) {
if (!n.getValue()) {
continue;
}
if (!n.isMeta()) {
continue;
}
if (!server.equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(server, includeGlobal, false)) {
continue;
}
}
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) {
return unescapeCharacters(meta.getValue());
}
}
return defaultValue;
}
private static void setChatMeta(boolean prefix, PermissionHolder holder, String value, int priority, String server, String world) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (value == null || value.equals("")) {
throw new IllegalArgumentException("value is null/empty");
}
Node.Builder node = LuckPerms.getApi().buildNode(prefix ? "prefix" : "suffix" + "." + priority + "." + escapeCharacters(value));
node.setValue(true);
if (!server.equalsIgnoreCase("global")) {
node.setServer(server);
}
if (world != null && !world.equals("")) {
node.setServer(server).setWorld(world);
}
try {
holder.setPermission(node.build());
} catch (ObjectAlreadyHasException ignored) {}
}
/**
* Adds a prefix to a holder on a specific server and world
* @param holder the holder to set the prefix for
* @param prefix the prefix value
* @param priority the priority to set the prefix at
* @param server the server to set the prefix on, can be null
* @param world the world to set the prefix on, can be null
* @throws NullPointerException if the holder is null
* @throws IllegalArgumentException if the prefix is null or empty
*/
public static void setPrefix(PermissionHolder holder, String prefix, int priority, String server, String world) {
setChatMeta(true, holder, prefix, priority, server, world);
}
/**
* Adds a suffix to a holder on a specific server and world
* @param holder the holder to set the suffix for
* @param suffix the suffix value
* @param priority the priority to set the suffix at
* @param server the server to set the suffix on, can be null
* @param world the world to set the suffix on, can be null
* @throws NullPointerException if the holder is null
* @throws IllegalArgumentException if the suffix is null or empty
*/
public static void setSuffix(PermissionHolder holder, String suffix, int priority, String server, String world) {
setChatMeta(false, holder, suffix, priority, server, world);
}
private static String getChatMeta(boolean prefix, PermissionHolder holder, String server, String world, boolean includeGlobal) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (server == null) {
server = "global";
}
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes()) {
if (!n.getValue()) {
continue;
}
if (!server.equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(server, includeGlobal, false)) {
continue;
}
}
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (value.getKey() > priority) {
meta = value.getValue();
priority = value.getKey();
}
}
return meta == null ? "" : unescapeCharacters(meta);
}
/**
* Returns a holders highest priority prefix, if they have one
* @param holder the holder
* @param server the server to retrieve the prefix on
* @param world the world to retrieve the prefix on
* @param includeGlobal if global nodes should be considered when retrieving the prefix
* @return a prefix string, if the holder has one, or an empty string if not.
* @throws NullPointerException if the holder is null
*/
public static String getPrefix(PermissionHolder holder, String server, String world, boolean includeGlobal) {
return getChatMeta(true, holder, server, world, includeGlobal);
}
/**
* Returns a holders highest priority suffix, if they have one
* @param holder the holder
* @param server the server to retrieve the suffix on
* @param world the world to retrieve the suffix on
* @param includeGlobal if global nodes should be considered when retrieving the suffix
* @return a suffix string, if the holder has one, or an empty string if not.
* @throws NullPointerException if the holder is null
*/
public static String getSuffix(PermissionHolder holder, String server, String world, boolean includeGlobal) {
return getChatMeta(false, holder, server, world, includeGlobal);
}
}
@@ -30,8 +30,9 @@ import java.util.Optional;
/**
* Represents an immutable node object
* <p> Use {@link LuckPermsApi#buildNode(String)} to get an instance.
* @since 1.6
* @since 2.6
*/
@SuppressWarnings("unused")
public interface Node extends Map.Entry<String, Boolean> {
/**
@@ -246,19 +247,38 @@ public interface Node extends Map.Entry<String, Boolean> {
Map.Entry<Integer, String> getSuffix();
/**
* Similar to {@link #equals(Object)}, except doesn't take note of the value
* @param node the other node
* @return true if the two nodes are almost equal
* Checks if this Node is equal to another node
* @param obj the other node
* @return true if this node is equal to the other provided
* @see #equalsIgnoringValue(Node) for a less strict implementation of this method
*/
boolean equalsIgnoringValue(Node node);
boolean equals(Object obj);
/**
* Similar to {@link #equals(Object)}, except doesn't take note of the expiry time or value
* @param node the other node
* Similar to {@link Node#equals(Object)}, except doesn't take note of the value
* @param other the other node
* @return true if the two nodes are almost equal
*/
boolean almostEquals(Node node);
boolean equalsIgnoringValue(Node other);
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the expiry time or value
* @param other the other node
* @return true if the two nodes are almost equal
*/
boolean almostEquals(Node other);
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the value or if the node is temporary
* @param other the other node
* @return true if the two nodes are almost equal
* @since 2.8
*/
boolean equalsIgnoringValueOrTemp(Node other);
/**
* Builds a Node instance
*/
interface Builder {
Builder setNegated(boolean negated);
Builder setValue(boolean value);
@@ -31,7 +31,7 @@ import java.util.Set;
import java.util.SortedSet;
/**
* Wrapper interface for internal PermissionHolder (user/group) instances
* Interface for internal PermissionHolder (user/group) instances
*/
@SuppressWarnings("unused")
public interface PermissionHolder {
@@ -45,21 +45,21 @@ public interface PermissionHolder {
/**
* Gets an immutable Set of the objects permission nodes
* @return an immutable set of permissions in priority order
* @since 1.6
* @since 2.6
*/
SortedSet<Node> getPermissions();
/**
* Similar to {@link #getPermissions()}, except excluding transient permissions
* @return a set of nodes
* @since 1.6
* @since 2.6
*/
Set<Node> getEnduringPermissions();
/**
* Similar to {@link #getPermissions()}, except excluding non-transient permissions
* @return a set of nodes
* @since 1.6
* @since 2.6
*/
Set<Node> getTransientPermissions();
@@ -67,7 +67,7 @@ public interface PermissionHolder {
/**
* Gets an immutable set of the nodes that this object has and inherits
* @return an immutable set of permissions
* @since 1.6
* @since 2.6
*/
Set<Node> getAllNodes();
@@ -84,7 +84,7 @@ public interface PermissionHolder {
* @param node the node to check for
* @return a Tristate for the holders permission status for the node
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
Tristate hasPermission(Node node);
@@ -93,7 +93,7 @@ public interface PermissionHolder {
* @param node the node to check for
* @return a Tristate for the holders permission status for the node
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
Tristate hasTransientPermission(Node node);
@@ -171,7 +171,7 @@ public interface PermissionHolder {
* @param node the node to check for
* @return a Tristate for the holders inheritance status for the node
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
Tristate inheritsPermission(Node node);
@@ -249,7 +249,7 @@ public interface PermissionHolder {
* @param node The node to be set
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
void setPermission(Node node) throws ObjectAlreadyHasException;
@@ -267,7 +267,7 @@ public interface PermissionHolder {
* @param node The node to be set
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
void setTransientPermission(Node node) throws ObjectAlreadyHasException;
@@ -357,7 +357,7 @@ public interface PermissionHolder {
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
void unsetPermission(Node node) throws ObjectLacksException;
@@ -366,7 +366,7 @@ public interface PermissionHolder {
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
void unsetTransientPermission(Node node) throws ObjectLacksException;
@@ -498,7 +498,7 @@ public interface PermissionHolder {
* @param possibleNodes a list of possible permissions for resolving wildcards
* @param applyGroups if inherited group permissions should be included
* @return a map of permissions
* @since 1.6
* @since 2.6
*/
Map<String, Boolean> getPermissions(String server, String world, Map<String, String> extraContext, boolean includeGlobal, List<String> possibleNodes, boolean applyGroups);
@@ -513,7 +513,7 @@ public interface PermissionHolder {
/**
* Processes the nodes and returns the temporary ones.
* @return a set of temporary nodes
* @since 1.6
* @since 2.6
*/
Set<Node> getTemporaryPermissionNodes();
@@ -528,7 +528,7 @@ public interface PermissionHolder {
/**
* Processes the nodes and returns the non-temporary ones.
* @return a set of permanent nodes
* @since 1.6
* @since 2.6
*/
Set<Node> getPermanentPermissionNodes();
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
/**
* Represents the platform type that LuckPerms is running on
* @since 2.7
*/
public enum PlatformType {
BUKKIT("Bukkit"),
BUNGEE("Bungee"),
SPONGE("Sponge");
private final String friendlyName;
PlatformType(String friendlyName) {
this.friendlyName = friendlyName;
}
public String getFriendlyName() {
return friendlyName;
}
}
@@ -28,7 +28,7 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
/**
* Wrapper interface for internal Track instances
* Interface for internal Track instances
*/
@SuppressWarnings("unused")
public interface Track {
@@ -29,7 +29,7 @@ import java.util.List;
import java.util.UUID;
/**
* Wrapper interface for internal User instances
* Interface for internal User instances
*/
@SuppressWarnings("unused")
public interface User extends PermissionHolder {
@@ -25,7 +25,9 @@ package me.lucko.luckperms.api;
import java.util.UUID;
/**
* This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode servers
* A UUID cache for online users, between external Mojang UUIDs, and internal LuckPerms UUIDs.
*
* <p> This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode servers
* or mixed offline mode and online mode servers. Platforms running in offline mode generate a random UUID for a user when
* they first join the server, but this UUID will then not be consistent across the network. LuckPerms will instead check
* the datastore cache, to get a UUID for a user that is consistent across an entire network.
@@ -35,6 +37,7 @@ import java.util.UUID;
*
* <p> WARNING: THIS IS ONLY EFFECTIVE FOR ONLINE PLAYERS. USE THE DATASTORE METHODS FOR OFFLINE PLAYERS.
*/
@SuppressWarnings("unused")
public interface UuidCache {
/**
@@ -26,7 +26,7 @@ import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.PermissionHolder;
/**
* @since 1.6
* @since 2.6
*/
public class AbstractPermissionEvent extends TargetedEvent<PermissionHolder> {
@@ -28,7 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/**
* Called when a temporary permission node expires
* @since 1.6
* @since 2.6
*/
public class PermissionNodeExpireEvent extends AbstractPermissionEvent {
public PermissionNodeExpireEvent(PermissionHolder target, Node node) {
@@ -28,7 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/**
* Called when a permission node is set on a holder
* @since 1.6
* @since 2.6
*/
public class PermissionNodeSetEvent extends AbstractPermissionEvent {
public PermissionNodeSetEvent(PermissionHolder target, Node node) {
@@ -28,7 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/**
* Called when a permission node is unset from a holder
* @since 1.6
* @since 2.6
*/
public class PermissionNodeUnsetEvent extends AbstractPermissionEvent {
public PermissionNodeUnsetEvent(PermissionHolder target, Node node) {
@@ -0,0 +1,8 @@
package me.lucko.luckperms.exceptions;
/**
* Thrown when a permission holding object doesn't / already has a permission or isn't / is already is a member of a group
* @since 2.7
*/
public abstract class MembershipException extends Exception {
}
@@ -26,5 +26,5 @@ package me.lucko.luckperms.exceptions;
* Thrown when a permission holding object already has a permission, is already a member of a group, or when a track
* already contains a group.
*/
public class ObjectAlreadyHasException extends Exception {
public class ObjectAlreadyHasException extends MembershipException {
}
@@ -26,5 +26,5 @@ package me.lucko.luckperms.exceptions;
* Thrown when a permission holding object does not already have a permission, is not already a member of a group,
* or when a track doesn't contain a group.
*/
public class ObjectLacksException extends Exception {
public class ObjectLacksException extends MembershipException {
}
+2 -2
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,7 +30,7 @@
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-api</artifactId>
<version>${project.version}</version>
<version>2.5</version>
<scope>compile</scope>
</dependency>
<!-- BukkitAPI -->
@@ -51,7 +51,6 @@ import java.util.regex.Pattern;
* - suffix
* - meta_<node>
*/
@SuppressWarnings("deprecation")
public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
private static final String IDENTIFIER = "luckperms";
private static final String PLUGIN_NAME = "LuckPerms";
@@ -76,7 +75,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
@Override
public String getVersion() {
return api == null ? "null" : api.getVersion();
return "2.5";
}
@Override
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -32,6 +32,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.*;
import org.bukkit.permissions.PermissionAttachment;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
class BukkitListener extends AbstractListener implements Listener {
@@ -45,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;
}
@@ -67,13 +68,19 @@ class BukkitListener extends AbstractListener implements Listener {
BukkitUser u = (BukkitUser) user;
PermissionAttachment attachment = player.addAttachment(plugin);
Map<String, Boolean> newPermMap = new ConcurrentHashMap<>();
try {
BukkitUser.getPermissionsField().set(attachment, new ConcurrentHashMap<>());
/* 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();
}
u.setAttachment(attachment);
u.setAttachment(new BukkitUser.PermissionAttachmentHolder(attachment, newPermMap));
}
plugin.doAsync(user::refreshPermissions);
@@ -98,4 +105,22 @@ class BukkitListener extends AbstractListener implements Listener {
plugin.getUserManager().getWorldCache().remove(e.getPlayer().getUniqueId());
onLeave(e.getPlayer().getUniqueId());
}
@EventHandler
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
if (plugin.getConfiguration().getEnableOps()) {
return;
}
String s = e.getMessage()
.replace("/", "")
.replace("bukkit:", "")
.replace("spigot:", "")
.replace("minecraft:", "");
if (s.startsWith("op") || s.startsWith("deop")) {
e.setCancelled(true);
e.getPlayer().sendMessage(Message.OP_DISABLED.toString());
}
}
}
@@ -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;
@@ -43,6 +44,8 @@ import me.lucko.luckperms.users.BukkitUserManager;
import me.lucko.luckperms.utils.LogFactory;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
@@ -132,6 +135,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
e.printStackTrace();
}
registerPermissions(getConfiguration().getCommandsAllowOp() ? PermissionDefault.OP : PermissionDefault.FALSE);
if (!getConfiguration().getEnableOps()) {
getServer().getOperators().forEach(o -> o.setOp(false));
}
getLog().info("Successfully loaded.");
}
@@ -161,8 +169,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
}
@Override
public Type getType() {
return Type.BUKKIT;
public PlatformType getType() {
return PlatformType.BUKKIT;
}
@Override
@@ -217,6 +225,16 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
return getServer().getServicesManager().load(clazz);
}
@SuppressWarnings("deprecation")
@Override
public UUID getUUID(String playerName) {
try {
return getServer().getOfflinePlayer(playerName).getUniqueId();
} catch (Exception e) {
return null;
}
}
@Override
public boolean isPluginLoaded(String name) {
return getServer().getPluginManager().isPluginEnabled(name);
@@ -226,4 +244,34 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
public void runUpdateTask() {
getServer().getScheduler().runTaskAsynchronously(this, new UpdateTask(this));
}
private void registerPermissions(PermissionDefault def) {
PluginManager pm = getServer().getPluginManager();
Map<String, List<String>> wildcards = new HashMap<>();
List<String> all = new ArrayList<>();
for (me.lucko.luckperms.constants.Permission p : me.lucko.luckperms.constants.Permission.values()) {
pm.addPermission(new Permission(p.getNode(), def));
if (p.getGroup() != null) {
if (!wildcards.containsKey(p.getGroup())) {
wildcards.put(p.getGroup(), new ArrayList<>());
}
wildcards.get(p.getGroup()).add(p.getTag());
}
all.add(p.getNode());
}
for (Map.Entry<String, List<String>> e : wildcards.entrySet()) {
pm.addPermission(new Permission(
"luckperms." + e.getKey() + ".*", def,
e.getValue().stream()
.map(tag -> "luckperms." + e.getKey() + "." + tag)
.collect(Collectors.toMap(s -> s, s -> false))
)
);
}
pm.addPermission(new Permission("luckperms.*", def, all.stream().collect(Collectors.toMap(s -> s, s -> false))));
}
}
@@ -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,113 +81,35 @@ 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) {
private String getMeta(PermissionHolder holder, String world, String node, String 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) {
if (holder == null) return defaultValue;
if (node.equals("")) return defaultValue;
node = escapeCharacters(node);
for (Node n : holder.getPermissions()) {
for (Node n : holder.getPermissions(true)) {
if (!n.getValue()) {
continue;
}
@@ -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.getLocalGroups("global", world).toArray(new String[0]) :
world != null && !world.equals("") ? user.getGroups(server, world, includeGlobal).toArray(new String[0]) :
user.getGroupNames().toArray(new String[0]);
}
@@ -22,6 +22,8 @@
package me.lucko.luckperms.users;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin;
@@ -52,7 +54,7 @@ public class BukkitUser extends User {
@Getter
@Setter
private PermissionAttachment attachment = null;
private PermissionAttachmentHolder attachment = null;
BukkitUser(UUID uuid, LPBukkitPlugin plugin) {
super(uuid, plugin);
@@ -72,11 +74,18 @@ public class BukkitUser extends User {
}
// Calculate the permissions that should be applied. This is done async, who cares about how long it takes or how often it's done.
Map<String, Boolean> toApply = getLocalPermissions(getPlugin().getConfiguration().getServer(), plugin.getUserManager().getWorldCache().get(getUuid()), null);
Map<String, Boolean> toApply = exportNodes(
getPlugin().getConfiguration().getServer(),
plugin.getUserManager().getWorldCache().get(getUuid()),
null,
plugin.getConfiguration().getIncludeGlobalPerms(),
true,
plugin.getPossiblePermissions()
);
try {
// The map in the LP PermissionAttachment is a ConcurrentHashMap. We can modify it's contents async.
Map<String, Boolean> existing = (Map<String, Boolean>) getPermissionsField().get(attachment);
// 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;
if (toApply.size() != existing.size()) {
@@ -99,12 +108,21 @@ public class BukkitUser extends User {
/* Must be called sync, as #recalculatePermissions is an unmodified Bukkit API call that is absolutely not thread safe.
Shouldn't be too taxing on the server. This only gets called when permissions have actually changed,
which is like once per user per login, assuming their permissions don't get modified. */
plugin.doSync(() -> attachment.getPermissible().recalculatePermissions());
plugin.doSync(() -> {
attachment.getAttachment().getPermissible().recalculatePermissions();
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
});
} catch (Exception e) {
e.printStackTrace();
}
}
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
@Getter
@EqualsAndHashCode
@AllArgsConstructor
public static class PermissionAttachmentHolder {
private final PermissionAttachment attachment;
private final Map<String, Boolean> permissions;
}
}
@@ -44,23 +44,18 @@ public class BukkitUserManager extends UserManager {
}
@Override
public void unload(User user) {
if (user != null) {
public void preUnload(User user) {
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
if (u.getAttachment() != null) {
Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid()));
if (u.getAttachment() != null) {
Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid()));
if (player != null) {
player.removeAttachment(u.getAttachment());
}
u.setAttachment(null);
if (player != null) {
player.removeAttachment(u.getAttachment().getAttachment());
}
u.setAttachment(null);
}
getAll().remove(user.getUuid());
}
}
+33 -1
View File
@@ -22,6 +22,10 @@ include-global: true
# 2. You are only running one server instance using LuckPerms, (not a network)
# In this case, set online-mode to true no matter what is set in server.properties. (we can just fallback to the servers uuid cache)
# 3. If your proxy is running in offline mode, and you are using PaperSpigot (https://ci.destroystokyo.com/job/PaperSpigot/),
# you should set "bungee-online-mode" to false in the paper.yml, and set "online-mode" to true in all LuckPerms configs.
# This approach is thoroughly recommended for offline mode networks.
online-mode: true
# If the plugin should apply wildcard permissions.
@@ -39,11 +43,39 @@ apply-regex: true
# If set to true, LuckPerms will detect and expand shorthand node patterns.
apply-shorthand: true
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true
# If the vanilla OP system is enabled. If set to false, all users will be de-opped, and the op/deop commands will be disabled.
enable-ops: true
# If opped players should be allowed to use LuckPerms commands. Set to false to only allow users with the permission access
commands-allow-op: 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, flatfile, mongodb
# Currently supported: mysql, sqlite, h2, json, yaml, mongodb
# Fill out connection info below if you're using MySQL or MongoDB
storage-method: h2
# This block enables support for split datastores.
# Only touch this if you're sure that you know what you're doing.
# I (the author) do not endorse nor recommend the use of this feature.
split-storage:
enabled: false
methods:
user: h2
group: h2
track: h2
uuid: h2
log: h2
data:
address: localhost:3306
database: minecraft
+9 -123
View File
@@ -1,130 +1,16 @@
name: LuckPerms
author: Luck
version: ${release.version}.${git.closest.tag.commit.count}
main: me.lucko.luckperms.LPBukkitPlugin
softdepend: [Vault, PermissionsEx, GroupManager, PowerfulPerms, zPermissions] # For migration
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
commands:
luckperms:
description: Manage permissions
aliases: [perms, permissions, lp, p, perm]
permissions:
luckperms.*:
description: Gives access to all LuckPerms commands
children:
luckperms.sync: true
luckperms.info: true
luckperms.debug: true
luckperms.import: true
luckperms.creategroup: true
luckperms.deletegroup: true
luckperms.listgroups: true
luckperms.createtrack: true
luckperms.deletetrack: true
luckperms.listtracks: true
luckperms.user.info: true
luckperms.user.getuuid: true
luckperms.user.listnodes: true
luckperms.user.haspermission: true
luckperms.user.inheritspermission: true
luckperms.user.setpermission: true
luckperms.user.unsetpermission: true
luckperms.user.addgroup: true
luckperms.user.removegroup: true
luckperms.user.settemppermission: true
luckperms.user.unsettemppermission: true
luckperms.user.addtempgroup: true
luckperms.user.removetempgroup: true
luckperms.user.setprimarygroup: true
luckperms.user.showtracks: true
luckperms.user.promote: true
luckperms.user.demote: true
luckperms.user.showpos: true
luckperms.user.clear: true
luckperms.group.info: true
luckperms.group.listnodes: true
luckperms.group.haspermission: true
luckperms.group.inheritspermission: true
luckperms.group.setpermission: true
luckperms.group.unsetpermission: true
luckperms.group.setinherit: true
luckperms.group.unsetinherit: true
luckperms.group.settemppermission: true
luckperms.group.unsettemppermission: true
luckperms.group.settempinherit: true
luckperms.group.unsettempinherit: true
luckperms.group.showtracks: true
luckperms.group.clear: true
luckperms.group.rename: true
luckperms.track.info: true
luckperms.track.append: true
luckperms.track.insert: true
luckperms.track.remove: true
luckperms.track.clear: true
luckperms.track.rename: true
luckperms.log.export: true
luckperms.log.userhistory: true
luckperms.log.grouphistory: true
luckperms.log.trackhistory: true
luckperms.log.notify: true
luckperms.log.recent: true
luckperms.log.search: true
luckperms.user.*:
description: Gives access to all LuckPerms user commands
children:
luckperms.user.info: true
luckperms.user.getuuid: true
luckperms.user.listnodes: true
luckperms.user.haspermission: true
luckperms.user.inheritspermission: true
luckperms.user.setpermission: true
luckperms.user.unsetpermission: true
luckperms.user.addgroup: true
luckperms.user.removegroup: true
luckperms.user.settemppermission: true
luckperms.user.unsettemppermission: true
luckperms.user.addtempgroup: true
luckperms.user.removetempgroup: true
luckperms.user.setprimarygroup: true
luckperms.user.showtracks: true
luckperms.user.promote: true
luckperms.user.demote: true
luckperms.user.showpos: true
luckperms.user.clear: true
luckperms.group.*:
description: Gives access to all LuckPerms group commands
children:
luckperms.group.info: true
luckperms.group.listnodes: true
luckperms.group.haspermission: true
luckperms.group.inheritspermission: true
luckperms.group.setpermission: true
luckperms.group.unsetpermission: true
luckperms.group.setinherit: true
luckperms.group.unsetinherit: true
luckperms.group.settemppermission: true
luckperms.group.unsettemppermission: true
luckperms.group.settempinherit: true
luckperms.group.unsettempinherit: true
luckperms.group.showtracks: true
luckperms.group.clear: true
luckperms.group.rename: true
luckperms.track.*:
description: Gives access to all LuckPerms track commands
children:
luckperms.track.info: true
luckperms.track.append: true
luckperms.track.insert: true
luckperms.track.remove: true
luckperms.track.clear: true
luckperms.track.rename: true
luckperms.log.*:
description: Gives access to all LuckPerms log commands
children:
luckperms.log.export: true
luckperms.log.userhistory: true
luckperms.log.grouphistory: true
luckperms.log.trackhistory: true
luckperms.log.notify: true
luckperms.log.recent: true
luckperms.log.search: true
# Permissions are registered programmatically instead of here.
+5 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -65,6 +65,10 @@
<pattern>org.h2</pattern>
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
</relocation>
<relocation>
<pattern>org.sqlite</pattern>
<shadedPattern>me.lucko.luckperms.lib.sqlite</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
@@ -33,7 +33,6 @@ import net.md_5.bungee.api.event.*;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
@@ -109,16 +108,10 @@ public class BungeeListener extends AbstractListener implements Listener {
@EventHandler
public void onPlayerPostLogin(PostLoginEvent e) {
final ProxiedPlayer player = e.getPlayer();
final WeakReference<ProxiedPlayer> p = new WeakReference<>(player);
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
if (user == null) {
plugin.getProxy().getScheduler().schedule(plugin, () -> {
final ProxiedPlayer pl = p.get();
if (pl != null) {
pl.sendMessage(WARN_MESSAGE);
}
}, 3, TimeUnit.SECONDS);
plugin.getProxy().getScheduler().schedule(plugin, () -> player.sendMessage(WARN_MESSAGE), 3, TimeUnit.SECONDS);
} else {
user.refreshPermissions();
}
@@ -24,6 +24,7 @@ package me.lucko.luckperms;
import lombok.Getter;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.ConsecutiveExecutor;
@@ -135,8 +136,8 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
}
@Override
public Type getType() {
return Type.BUNGEE;
public PlatformType getType() {
return PlatformType.BUNGEE;
}
@Override
@@ -185,6 +186,11 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
return null;
}
@Override
public UUID getUUID(String playerName) {
return null; // Not needed on Bungee
}
@Override
public boolean isPluginLoaded(String name) {
return getProxy().getPluginManager().getPlugins().stream()
@@ -34,15 +34,6 @@ public class BungeeUserManager extends UserManager {
this.plugin = plugin;
}
@Override
public void unload(User user) {
if (user != null) {
// Cannot clear the ProxiedPlayer's permission map, they're leaving so that will get GCed anyway
// Calling getPermissions.clear() throws an UnsupportedOperationException
getAll().remove(user.getUuid());
}
}
@Override
public void cleanup(User user) {
if (plugin.getProxy().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())) == null) {
+20 -1
View File
@@ -22,6 +22,10 @@ include-global: false
# 2. You are only running one server instance using LuckPerms, (not a network)
# In this case, set online-mode to true no matter what is set in server.properties. (we can just fallback to the servers uuid cache)
# 3. If your proxy is running in offline mode, and you are using PaperSpigot (https://ci.destroystokyo.com/job/PaperSpigot/),
# you should set "bungee-online-mode" to false in the paper.yml, and set "online-mode" to true in all LuckPerms configs.
# This approach is thoroughly recommended for offline mode networks.
online-mode: true
# If the plugin should apply wildcard permissions.
@@ -39,11 +43,26 @@ apply-regex: true
# If set to true, LuckPerms will detect and expand shorthand node patterns.
apply-shorthand: true
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true
# Which storage method the plugin should use.
# Currently supported: mysql, sqlite, h2, flatfile, mongodb
# Currently supported: mysql, sqlite, h2, json, yaml, mongodb
# Fill out connection info below if you're using MySQL or MongoDB
storage-method: h2
# This block enables support for split datastores.
# Only touch this if you're sure that you know what you're doing.
# I (the author) do not endorse nor recommend the use of this feature.
split-storage:
enabled: false
methods:
user: h2
group: h2
track: h2
uuid: h2
log: h2
data:
address: localhost:3306
database: minecraft
+3 -3
View File
@@ -1,6 +1,6 @@
name: LuckPerms
author: Luck
version: ${release.version}.${git.closest.tag.commit.count}
main: me.lucko.luckperms.LPBungeePlugin
softdepend: [PowerfulPerms] # For migration
description: A permissions plugin
author: Luck
main: me.lucko.luckperms.LPBungeePlugin
softdepends: [PowerfulPerms] # For migration
+18 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -67,6 +67,13 @@
<version>1.7.9</version>
<scope>compile</scope>
</dependency>
<!-- SnakeYAML -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.14</version>
<scope>provided</scope>
</dependency>
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
@@ -105,6 +112,10 @@
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>net.gravitydevelopment.updater</groupId>
<artifactId>updater</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -125,6 +136,12 @@
<version>3.0-dev-52</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>de.bananaco</groupId>
<artifactId>bpermissions-api</artifactId>
<version>2.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -23,6 +23,7 @@
package me.lucko.luckperms;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.commands.ConsecutiveExecutor;
import me.lucko.luckperms.commands.Sender;
@@ -65,11 +66,10 @@ public interface LuckPermsPlugin {
*/
String getVersion();
/**
* @return the platform type
*/
Type getType();
PlatformType getType();
/**
* @return the main plugin directory
@@ -136,6 +136,13 @@ public interface LuckPermsPlugin {
*/
Object getService(Class clazz);
/**
* Used as a backup for migration
* @param playerName the players name
* @return a uuid if found, or null if not
*/
UUID getUUID(String playerName);
/**
* Checks if a plugin is loaded on the platform
* @param name the name of the plugin
@@ -160,8 +167,4 @@ public interface LuckPermsPlugin {
*/
void doSync(Runnable r);
enum Type {
BUKKIT, BUNGEE, SPONGE;
}
}
@@ -39,7 +39,7 @@ import java.util.stream.Collectors;
import static me.lucko.luckperms.api.implementation.internal.Utils.checkNode;
/**
* Provides static access to LuckPerms
* Implements the LuckPerms API using the plugin instance
*/
@AllArgsConstructor
public class ApiProvider implements LuckPermsApi {
@@ -67,7 +67,7 @@ public class ApiProvider implements LuckPermsApi {
@Override
public double getApiVersion() {
return 1.6;
return 2.8;
}
@Override
@@ -75,6 +75,11 @@ public class ApiProvider implements LuckPermsApi {
return plugin.getVersion();
}
@Override
public PlatformType getPlatformType() {
return plugin.getType();
}
@Override
public void registerListener(@NonNull LPListener listener) {
eventBus.register(listener);
@@ -27,6 +27,8 @@ import me.lucko.luckperms.api.LPConfiguration;
import me.lucko.luckperms.api.data.DatastoreConfiguration;
import me.lucko.luckperms.api.data.MySQLConfiguration;
import java.util.Map;
/**
* Provides a link between {@link LPConfiguration} and {@link me.lucko.luckperms.core.LPConfiguration}
*/
@@ -79,6 +81,31 @@ public class LPConfigurationLink implements LPConfiguration {
return master.getApplyShorthand();
}
@Override
public boolean getLogNotify() {
return master.getLogNotify();
}
@Override
public boolean getEnableOps() {
return master.getEnableOps();
}
@Override
public boolean getCommandsAllowOp() {
return master.getCommandsAllowOp();
}
@Override
public String getVaultServer() {
return master.getVaultServer();
}
@Override
public boolean getVaultIncludeGlobal() {
return master.getVaultIncludeGlobal();
}
@SuppressWarnings("deprecation")
@Override
public MySQLConfiguration getDatabaseValues() {
@@ -94,4 +121,15 @@ public class LPConfigurationLink implements LPConfiguration {
public String getStorageMethod() {
return master.getStorageMethod();
}
@Override
public boolean getSplitStorage() {
return master.getSplitStorage();
}
@SuppressWarnings("unchecked")
@Override
public Map<String, String> getSplitStorageOptions() {
return master.getSplitStorageOptions();
}
}
@@ -52,7 +52,7 @@ public class PermissionHolderLink implements PermissionHolder {
@Override
public SortedSet<Node> getPermissions() {
return Collections.unmodifiableSortedSet(master.getPermissions());
return Collections.unmodifiableSortedSet(master.getPermissions(false));
}
@Override
@@ -22,11 +22,14 @@
package me.lucko.luckperms.commands;
import com.google.common.collect.ImmutableMap;
import lombok.Getter;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.constants.Permission;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/**
@@ -35,7 +38,7 @@ import java.util.stream.Collectors;
*/
public abstract class SenderFactory<T> implements Runnable {
private final Map<T, List<String>> messages = new HashMap<>();
//private final List<Map.Entry<T, String>> messages = new ArrayList<>();
private final AtomicBoolean shouldSend = new AtomicBoolean(false);
private final SenderFactory<T> factory = this;
protected abstract String getName(T t);
@@ -44,50 +47,70 @@ public abstract class SenderFactory<T> implements Runnable {
protected abstract boolean hasPermission(T t, String node);
public final Sender wrap(T t) {
return new Sender() {
final WeakReference<T> tRef = new WeakReference<>(t);
// Cache these permissions, so they can be accessed async
final Map<Permission, Boolean> perms = Arrays.stream(Permission.values())
.map(p -> new AbstractMap.SimpleEntry<>(p, factory.hasPermission(t, p.getNode())))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
@Getter
final String name = factory.getName(t);
@Getter
final UUID uuid = factory.getUuid(t);
@Override
public void sendMessage(String s) {
final T t = tRef.get();
if (t != null) {
synchronized (messages) {
if (!messages.containsKey(t)) {
messages.put(t, new ArrayList<>());
}
messages.get(t).add(s);
}
}
}
@Override
public boolean hasPermission(Permission permission) {
synchronized (perms) {
return perms.get(permission);
}
}
};
return new SenderImp(t);
}
@Override
public final void run() {
if (!shouldSend.getAndSet(false)) {
return;
}
synchronized (messages) {
if (!messages.isEmpty()) {
messages.entrySet().forEach(e -> e.getValue().forEach(s -> factory.sendMessage(e.getKey(), s)));
messages.clear();
for (Map.Entry<T, List<String>> e : messages.entrySet()) {
for (String s : e.getValue()) {
factory.sendMessage(e.getKey(), s);
}
}
messages.clear();
}
}
private class SenderImp implements Sender {
private final WeakReference<T> tRef;
// Cache these permissions, so they can be accessed async
private Map<Permission, Boolean> perms;
@Getter
private final String name;
@Getter
private final UUID uuid;
private final boolean console;
private SenderImp(T t) {
this.tRef = new WeakReference<>(t);
this.name = factory.getName(t);
this.uuid = factory.getUuid(t);
this.console = this.uuid.equals(Constants.getConsoleUUID()) || this.uuid.equals(Constants.getImporterUUID());
if (!this.console) {
this.perms = ImmutableMap.copyOf(Arrays.stream(Permission.values())
.collect(Collectors.toMap(p -> p, p -> factory.hasPermission(t, p.getNode()))));
}
}
@Override
public void sendMessage(String s) {
final T t = tRef.get();
if (t != null) {
synchronized (messages) {
if (!messages.containsKey(t)) {
messages.put(t, new ArrayList<>());
}
messages.get(t).add(s);
}
shouldSend.set(true);
}
}
@Override
public boolean hasPermission(Permission permission) {
return console || perms.get(permission);
}
}
}
@@ -110,10 +110,11 @@ public abstract class SubCommand<T> {
return permission.isAuthorized(sender);
}
/*
----------------------------------------------------------------------------------
Utility methods used by #onTabComplete and #execute implementations in sub classes
----------------------------------------------------------------------------------
* ----------------------------------------------------------------------------------
* Utility methods used by #onTabComplete and #execute implementations in sub classes
* ----------------------------------------------------------------------------------
*/
protected static List<String> getGroupTabComplete(List<String> args, LuckPermsPlugin plugin) {
@@ -23,13 +23,12 @@
package me.lucko.luckperms.commands;
import lombok.experimental.UtilityClass;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.utils.DateUtil;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
@UtilityClass
public class Util {
@@ -114,35 +113,53 @@ public class Util {
return sb.delete(sb.length() - 6, sb.length()).toString();
}
public static String permNodesToString(Map<String, Boolean> nodes) {
if (nodes.isEmpty()) return "&6None";
public static String permNodesToString(SortedSet<Node> nodes) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
if (e.getValue()) {
sb.append("&a").append(e.getKey()).append("&7, ");
} else {
sb.append("&c").append(e.getKey()).append("&7, ");
for (Node node : nodes) {
if (node.isTemporary()) {
continue;
}
sb.append("&6-> ").append(node.getValue() ? "&a" : "&c");
sb.append(node.getPermission());
if (node.isServerSpecific()) {
sb.append(" &7(&f").append(node.getServer().get()).append("&7)");
}
if (node.isWorldSpecific()) {
sb.append(" &7(&f").append(node.getWorld().get()).append("&7)");
}
sb.append("\n");
}
return sb.delete(sb.length() - 2, sb.length()).toString();
if (sb.length() == 0) {
return "&6None";
}
return sb.toString();
}
public static String tempNodesToString(Map<Map.Entry<String, Boolean>, Long> nodes) {
if (nodes.isEmpty()) return "&6None";
public static String tempNodesToString(SortedSet<Node> nodes) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<Map.Entry<String, Boolean>, Long> e : nodes.entrySet()) {
if (e.getKey().getValue()) {
sb.append("&a").append(e.getKey().getKey()).append("&6 - expires in ")
.append(DateUtil.formatDateDiff(e.getValue())).append("\n");
} else {
sb.append("&c").append(e.getKey().getKey()).append("&6 - expires in ")
.append(DateUtil.formatDateDiff(e.getValue())).append("\n");
for (Node node : nodes) {
if (!node.isTemporary()) {
continue;
}
sb.append("&6-> ").append(node.getValue() ? "&a" : "&c");
sb.append(node.getPermission());
if (node.isServerSpecific()) {
sb.append(" &7(&f").append(node.getServer().get()).append("&7)");
}
if (node.isWorldSpecific()) {
sb.append(" &7(&f").append(node.getWorld().get()).append("&7)");
}
sb.append("&6 - expires in ").append(DateUtil.formatDateDiff(node.getExpiryUnixTime())).append("\n");
}
if (sb.length() == 0) {
return "&6None";
}
return sb.toString();
@@ -161,4 +178,25 @@ public class Util {
}
}
}
public static synchronized MetaComparator getMetaComparator() {
if (metaComparator == null) {
metaComparator = new MetaComparator();
}
return metaComparator;
}
private static MetaComparator metaComparator = null;
public class MetaComparator implements Comparator<Map.Entry<Integer, String>> {
@Override
public int compare(Map.Entry<Integer, String> o1, Map.Entry<Integer, String> o2) {
int result = Integer.compare(o1.getKey(), o2.getKey());
if (result == 0) {
result = o1.getValue().compareTo(o2.getValue());
}
return result;
}
}
}
@@ -50,6 +50,15 @@ public class GroupMainCommand extends MainCommand<Group> {
.add(new GroupSetTempInherit())
.add(new GroupUnsetTempInherit())
.add(new GroupShowTracks())
.add(new GroupChatMeta())
.add(new GroupAddPrefix())
.add(new GroupAddSuffix())
.add(new GroupRemovePrefix())
.add(new GroupRemoveSuffix())
.add(new GroupAddTempPrefix())
.add(new GroupAddTempSuffix())
.add(new GroupRemoveTempPrefix())
.add(new GroupRemoveTempSuffix())
.add(new GroupClear())
.add(new GroupRename())
.build()
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class GroupAddPrefix extends SubCommand<Group> {
public GroupAddPrefix() {
super("addprefix", "Adds a prefix to the group", "/%s group <group> addprefix <priority> <prefix> [server] [world]",
Permission.GROUP_ADDPREFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
group.setPermission(node, true, server);
Message.ADDPREFIX_SERVER_SUCCESS.send(sender, group.getName(), prefix, priority, server);
LogEntry.build().actor(sender).acted(group)
.action("addprefix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
group.setPermission(node, true, server, world);
Message.ADDPREFIX_SERVER_WORLD_SUCCESS.send(sender, group.getName(), prefix, priority, server, world);
LogEntry.build().actor(sender).acted(group)
.action("addprefix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.setPermission(node, true);
Message.ADDPREFIX_SUCCESS.send(sender, group.getName(), prefix, priority);
LogEntry.build().actor(sender).acted(group)
.action("addprefix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_PREFIX.send(sender, group.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class GroupAddSuffix extends SubCommand<Group> {
public GroupAddSuffix() {
super("addsuffix", "Adds a suffix to the group", "/%s group <group> addsuffix <priority> <suffix> [server] [world]",
Permission.GROUP_ADDSUFFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
group.setPermission(node, true, server);
Message.ADDSUFFIX_SERVER_SUCCESS.send(sender, group.getName(), suffix, priority, server);
LogEntry.build().actor(sender).acted(group)
.action("addsuffix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
group.setPermission(node, true, server, world);
Message.ADDSUFFIX_SERVER_WORLD_SUCCESS.send(sender, group.getName(), suffix, priority, server, world);
LogEntry.build().actor(sender).acted(group)
.action("addsuffix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.setPermission(node, true);
Message.ADDSUFFIX_SUCCESS.send(sender, group.getName(), suffix, priority);
LogEntry.build().actor(sender).acted(group)
.action("addsuffix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_SUFFIX.send(sender, group.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import me.lucko.luckperms.utils.DateUtil;
import java.util.List;
public class GroupAddTempPrefix extends SubCommand<Group> {
public GroupAddTempPrefix() {
super("addtempprefix", "Adds a prefix to the group temporarily", "/%s group <group> addtempprefix <priority> <prefix> <duration> [server] [world]",
Permission.GROUP_ADD_TEMP_PREFIX, Predicate.notInRange(3, 5));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
long duration;
try {
duration = Long.parseLong(args.get(2));
} catch (NumberFormatException e) {
try {
duration = DateUtil.parseDateDiff(args.get(2), true);
} catch (DateUtil.IllegalDateException e1) {
Message.ILLEGAL_DATE_ERROR.send(sender, args.get(2));
return CommandResult.INVALID_ARGS;
}
}
if (DateUtil.shouldExpire(duration)) {
Message.PAST_DATE_ERROR.send(sender);
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 4) {
final String server = args.get(3).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 4) {
group.setPermission(node, true, server, duration);
Message.ADD_TEMP_PREFIX_SERVER_SUCCESS.send(sender, group.getName(), prefix, priority, server, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempprefix " + priority + " " + args.get(1) + " " + duration + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(4).toLowerCase();
group.setPermission(node, true, server, world, duration);
Message.ADD_TEMP_PREFIX_SERVER_WORLD_SUCCESS.send(sender, group.getName(), prefix, priority, server, world, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempprefix " + priority + " " + args.get(1) + " " + duration + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.setPermission(node, true, duration);
Message.ADD_TEMP_PREFIX_SUCCESS.send(sender, group.getName(), prefix, priority, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempprefix " + priority + " " + args.get(1) + " " + duration)
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_PREFIX.send(sender, group.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import me.lucko.luckperms.utils.DateUtil;
import java.util.List;
public class GroupAddTempSuffix extends SubCommand<Group> {
public GroupAddTempSuffix() {
super("addtempsuffix", "Adds a suffix to the group temporarily", "/%s group <group> addtempsuffix <priority> <suffix> <duration> [server] [world]",
Permission.GROUP_ADD_TEMP_SUFFIX, Predicate.notInRange(3, 5));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
long duration;
try {
duration = Long.parseLong(args.get(2));
} catch (NumberFormatException e) {
try {
duration = DateUtil.parseDateDiff(args.get(2), true);
} catch (DateUtil.IllegalDateException e1) {
Message.ILLEGAL_DATE_ERROR.send(sender, args.get(2));
return CommandResult.INVALID_ARGS;
}
}
if (DateUtil.shouldExpire(duration)) {
Message.PAST_DATE_ERROR.send(sender);
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 4) {
final String server = args.get(3).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 4) {
group.setPermission(node, true, server, duration);
Message.ADD_TEMP_SUFFIX_SERVER_SUCCESS.send(sender, group.getName(), suffix, priority, server, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempsuffix " + priority + " " + args.get(1) + " " + duration + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(4).toLowerCase();
group.setPermission(node, true, server, world, duration);
Message.ADD_TEMP_SUFFIX_SERVER_WORLD_SUCCESS.send(sender, group.getName(), suffix, priority, server, world, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempsuffix " + priority + " " + args.get(1) + " " + duration + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.setPermission(node, true, duration);
Message.ADD_TEMP_SUFFIX_SUCCESS.send(sender, group.getName(), suffix, priority, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempsuffix " + priority + " " + args.get(1) + " " + duration)
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_SUFFIX.send(sender, group.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.groups.Group;
import java.util.*;
public class GroupChatMeta extends SubCommand<Group> {
public GroupChatMeta() {
super("chatmeta", "Displays a groups chat meta", "/%s group <group> chatmeta", Permission.GROUP_CHATMETA,
Predicate.alwaysFalse());
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
SortedSet<Map.Entry<Integer, String>> prefixes = new TreeSet<>(Util.getMetaComparator().reversed());
SortedSet<Map.Entry<Integer, String>> suffixes = new TreeSet<>(Util.getMetaComparator().reversed());
for (Node node : group.getAllNodes(null)) {
if (!node.isSuffix() && !node.isPrefix()) {
continue;
}
StringBuilder sb = new StringBuilder();
if (node.isServerSpecific()) {
if (node.isWorldSpecific()) {
sb.append("&6W=").append(node.getWorld().get()).append(" ");
}
sb.append("&6S=").append(node.getServer().get()).append(" ");
}
if (node.isPrefix()) {
sb.append(node.getPrefix().getValue());
prefixes.add(new AbstractMap.SimpleEntry<>(node.getPrefix().getKey(), sb.toString()));
}
if (node.isSuffix()) {
sb.append(node.getSuffix().getValue());
suffixes.add(new AbstractMap.SimpleEntry<>(node.getSuffix().getKey(), sb.toString()));
}
}
if (prefixes.isEmpty()) {
Message.CHAT_META_PREFIX_NONE.send(sender, group.getName());
} else {
Message.CHAT_META_PREFIX_HEADER.send(sender, group.getName());
for (Map.Entry<Integer, String> e : prefixes) {
Message.CHAT_META_ENTRY.send(sender, e.getKey(), e.getValue());
}
}
if (suffixes.isEmpty()) {
Message.CHAT_META_SUFFIX_NONE.send(sender, group.getName());
} else {
Message.CHAT_META_SUFFIX_HEADER.send(sender, group.getName());
for (Map.Entry<Integer, String> e : suffixes) {
Message.CHAT_META_ENTRY.send(sender, e.getKey(), e.getValue());
}
}
return CommandResult.SUCCESS;
}
}
@@ -30,8 +30,6 @@ import me.lucko.luckperms.groups.Group;
import java.util.List;
import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
public class GroupListNodes extends SubCommand<Group> {
public GroupListNodes() {
super("listnodes", "Lists the permission nodes the group has", "/%s group <group> listnodes",
@@ -40,8 +38,8 @@ public class GroupListNodes extends SubCommand<Group> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
Message.LISTNODES.send(sender, group.getName(), Util.permNodesToString(exportToLegacy(group.getPermanentNodes())));
Message.LISTNODES_TEMP.send(sender, group.getName(), Util.tempNodesToString(group.getTemporaryNodesLegacy()));
Message.LISTNODES.send(sender, group.getName(), Util.permNodesToString(group.getPermissions(false)));
Message.LISTNODES_TEMP.send(sender, group.getName(), Util.tempNodesToString(group.getPermissions(false)));
return CommandResult.SUCCESS;
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class GroupRemovePrefix extends SubCommand<Group> {
public GroupRemovePrefix() {
super("removeprefix", "Removes a prefix from a group", "/%s group <group> removeprefix <priority> <prefix> [server] [world]",
Permission.GROUP_REMOVEPREFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
group.unsetPermission(node, server);
Message.REMOVEPREFIX_SERVER_SUCCESS.send(sender, group.getName(), prefix, priority, server);
LogEntry.build().actor(sender).acted(group)
.action("removeprefix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
group.unsetPermission(node, server, world);
Message.REMOVEPREFIX_SERVER_WORLD_SUCCESS.send(sender, group.getName(), prefix, priority, server, world);
LogEntry.build().actor(sender).acted(group)
.action("removeprefix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.unsetPermission(node);
Message.REMOVEPREFIX_SUCCESS.send(sender, group.getName(), prefix, priority);
LogEntry.build().actor(sender).acted(group)
.action("removeprefix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectLacksException e) {
Message.DOES_NOT_HAVE_PREFIX.send(sender, group.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class GroupRemoveSuffix extends SubCommand<Group> {
public GroupRemoveSuffix() {
super("removesuffix", "Removes a suffix from a group", "/%s group <group> removesuffix <priority> <suffix> [server] [world]",
Permission.GROUP_REMOVESUFFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
group.unsetPermission(node, server);
Message.REMOVESUFFIX_SERVER_SUCCESS.send(sender, group.getName(), suffix, priority, server);
LogEntry.build().actor(sender).acted(group)
.action("removesuffix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
group.unsetPermission(node, server, world);
Message.REMOVESUFFIX_SERVER_WORLD_SUCCESS.send(sender, group.getName(), suffix, priority, server, world);
LogEntry.build().actor(sender).acted(group)
.action("removesuffix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.unsetPermission(node);
Message.REMOVESUFFIX_SUCCESS.send(sender, group.getName(), suffix, priority);
LogEntry.build().actor(sender).acted(group)
.action("removesuffix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectLacksException e) {
Message.DOES_NOT_HAVE_SUFFIX.send(sender, group.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class GroupRemoveTempPrefix extends SubCommand<Group> {
public GroupRemoveTempPrefix() {
super("removetempprefix", "Removes a temporary prefix from a group", "/%s group <group> removetempprefix <priority> <prefix> [server] [world]",
Permission.GROUP_REMOVE_TEMP_PREFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
group.unsetPermission(node, server, true);
Message.REMOVE_TEMP_PREFIX_SERVER_SUCCESS.send(sender, group.getName(), prefix, priority, server);
LogEntry.build().actor(sender).acted(group)
.action("removetempprefix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
group.unsetPermission(node, server, world, true);
Message.REMOVE_TEMP_PREFIX_SERVER_WORLD_SUCCESS.send(sender, group.getName(), prefix, priority, server, world);
LogEntry.build().actor(sender).acted(group)
.action("removetempprefix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.unsetPermission(node, true);
Message.REMOVE_TEMP_PREFIX_SUCCESS.send(sender, group.getName(), prefix, priority);
LogEntry.build().actor(sender).acted(group)
.action("removetempprefix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectLacksException e) {
Message.DOES_NOT_HAVE_PREFIX.send(sender, group.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class GroupRemoveTempSuffix extends SubCommand<Group> {
public GroupRemoveTempSuffix() {
super("removetempsuffix", "Removes a temporary suffix from a group", "/%s group <group> removetempsuffix <priority> <suffix> [server] [world]",
Permission.GROUP_REMOVE_TEMP_SUFFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
group.unsetPermission(node, server, true);
Message.REMOVE_TEMP_SUFFIX_SERVER_SUCCESS.send(sender, group.getName(), suffix, priority, server);
LogEntry.build().actor(sender).acted(group)
.action("removetempsuffix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
group.unsetPermission(node, server, world, true);
Message.REMOVE_TEMP_SUFFIX_SERVER_WORLD_SUCCESS.send(sender, group.getName(), suffix, priority, server, world);
LogEntry.build().actor(sender).acted(group)
.action("removetempsuffix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.unsetPermission(node, true);
Message.REMOVE_TEMP_SUFFIX_SUCCESS.send(sender, group.getName(), suffix, priority);
LogEntry.build().actor(sender).acted(group)
.action("removetempsuffix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectLacksException e) {
Message.DOES_NOT_HAVE_SUFFIX.send(sender, group.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -108,7 +108,7 @@ public class LogExport extends SubCommand<Log> {
break track;
}
b.append("track ").append(e.getActedName()).append(" ").append(e.getAction());;
b.append("track ").append(e.getActedName()).append(" ").append(e.getAction());
}
data.add(b.toString());
@@ -66,6 +66,11 @@ public class MigrationMainCommand extends MainCommand<Object> {
subCommands.add(new MigrationBungeePerms());
} catch (Throwable ignored) {}
try {
Class.forName("de.bananaco.bpermissions.api.WorldManager");
subCommands.add(new MigrationBPermissions());
} catch (Throwable ignored) {}
}
@Override
@@ -0,0 +1,231 @@
package me.lucko.luckperms.commands.migration.subcommands;
import de.bananaco.bpermissions.api.*;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import static me.lucko.luckperms.constants.Permission.MIGRATION;
public class MigrationBPermissions extends SubCommand<Object> {
private static Field uConfigField;
private static Method getConfigurationSectionMethod = null;
private static Method getKeysMethod = null;
static {
try {
uConfigField = Class.forName("de.bananaco.bpermissions.imp.YamlWorld").getDeclaredField("uconfig");
uConfigField.setAccessible(true);
} catch (Throwable t) {
t.printStackTrace();
}
}
@SuppressWarnings("unchecked")
private static Set<String> getUsers(World world) {
try {
Object yamlWorldUsers = uConfigField.get(world);
if (getConfigurationSectionMethod == null) {
getConfigurationSectionMethod = yamlWorldUsers.getClass().getMethod("getConfigurationSection", String.class);
getConfigurationSectionMethod.setAccessible(true);
}
Object configSection = getConfigurationSectionMethod.invoke(yamlWorldUsers, "users");
if (configSection == null) {
return Collections.emptySet();
}
if (getKeysMethod == null) {
getKeysMethod = configSection.getClass().getMethod("getKeys", boolean.class);
getKeysMethod.setAccessible(true);
}
return (Set<String>) getKeysMethod.invoke(configSection, false);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
public MigrationBPermissions() {
super("bpermissions", "Migration from bPermissions", "/%s migration bpermissions", MIGRATION, Predicate.alwaysFalse());
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
final Logger log = plugin.getLog();
WorldManager worldManager = WorldManager.getInstance();
if (worldManager == null) {
log.severe("bPermissions Migration: Error -> bPermissions is not loaded.");
return CommandResult.STATE_ERROR;
}
log.info("bPermissions Migration: Forcing the plugin to load all data. This could take a while.");
for (World world : worldManager.getAllWorlds()) {
Set<String> users = getUsers(world);
if (users == null) {
log.severe("bPermissions Migration: Couldn't get a list of users.");
return CommandResult.FAILURE;
}
users.forEach(s -> world.loadOne(s, CalculableType.USER));
}
// Migrate one world at a time.
log.info("bPermissions Migration: Starting world migration.");
for (World world : worldManager.getAllWorlds()) {
log.info("bPermissions Migration: Migrating world: " + world.getName());
// Migrate all groups
log.info("bPermissions Migration: Starting group migration in world " + world.getName() + ".");
int groupCount = 0;
for (Calculable group : world.getAll(CalculableType.GROUP)) {
groupCount++;
String groupName = group.getName().toLowerCase();
if (group.getName().equalsIgnoreCase(world.getDefaultGroup())) {
groupName = "default";
}
// Make a LuckPerms group for the one being migrated.
plugin.getDatastore().createAndLoadGroup(groupName);
me.lucko.luckperms.groups.Group lpGroup = plugin.getGroupManager().get(groupName);
try {
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(lpGroup).action("create")
.build().submit(plugin);
} catch (Exception ex) {
ex.printStackTrace();
}
migrateHolder(plugin, world, group, lpGroup);
plugin.getDatastore().saveGroup(lpGroup);
}
log.info("bPermissions Migration: Migrated " + groupCount + " groups in world " + world.getName() + ".");
// Migrate all users
log.info("bPermissions Migration: Starting user migration in world " + world.getName() + ".");
int userCount = 0;
for (Calculable user : world.getAll(CalculableType.USER)) {
userCount++;
// There is no mention of UUIDs in the API. I assume that name = uuid. idk?
UUID uuid;
try {
uuid = UUID.fromString(user.getName());
} catch (IllegalArgumentException e) {
uuid = plugin.getUUID(user.getName());
}
if (uuid == null) {
log.info("bPermissions Migration: Unable to migrate user " + user.getName() + ". Unable to get UUID.");
continue;
}
// Make a LuckPerms user for the one being migrated.
plugin.getDatastore().loadUser(uuid, "null");
me.lucko.luckperms.users.User lpUser = plugin.getUserManager().get(uuid);
migrateHolder(plugin, world, user, lpUser);
plugin.getDatastore().saveUser(lpUser);
plugin.getUserManager().cleanup(lpUser);
}
log.info("bPermissions Migration: Migrated " + userCount + " users in world " + world.getName() + ".");
}
log.info("bPermissions Migration: Success! Completed without any errors.");
return CommandResult.SUCCESS;
}
private static void migrateHolder(LuckPermsPlugin plugin, World world, Calculable c, PermissionHolder holder) {
// Migrate the groups permissions in this world
for (Permission p : c.getPermissions()) {
try {
holder.setPermission(p.name(), p.isTrue(), "global", world.getName());
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("set " + p.name() + " " + p.isTrue() + " global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
// Include any child permissions
for (Map.Entry<String, Boolean> child : p.getChildren().entrySet()) {
try {
holder.setPermission(child.getKey(), child.getValue(), "global", world.getName());
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("set " + child.getKey() + " " + child.getValue() + " global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
// Migrate any inherited groups
for (Group parent : c.getGroups()) {
try {
holder.setPermission("group." + parent.getName(), true, "global", world.getName());
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("setinherit " + parent.getName() + " global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
// Migrate existing meta
for (Map.Entry<String, String> meta : c.getMeta().entrySet()) {
if (meta.getKey().equalsIgnoreCase("prefix") || meta.getKey().equalsIgnoreCase("suffix")) {
String chatMeta = ArgumentChecker.escapeCharacters(meta.getValue());
try {
holder.setPermission(meta.getKey().toLowerCase() + "." + c.getPriority() + "." + chatMeta, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("set " + meta.getKey().toLowerCase() + "." + c.getPriority() + "." + chatMeta + " true")
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
continue;
}
try {
holder.setPermission("meta." + meta.getKey() + "." + meta.getValue(), true, "global", world.getName());
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("set meta." + meta.getKey() + "." + meta.getValue() + " true global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
}
@@ -24,6 +24,7 @@ package me.lucko.luckperms.commands.migration.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
@@ -43,41 +44,11 @@ import ru.tehkode.permissions.bukkit.PermissionsEx;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public class MigrationPermissionsEx extends SubCommand<Object> {
private static Class<?> bukkitPlayer = null;
private static Method getUniqueIdMethod = null;
private static Method getPlayerMethod = null;
static {
try {
bukkitPlayer = Class.forName("org.bukkit.entity.Player");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (bukkitPlayer != null) {
try {
getUniqueIdMethod = bukkitPlayer.getMethod("getUniqueId");
getUniqueIdMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
try {
getPlayerMethod = PermissionUser.class.getMethod("getPlayer");
getPlayerMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
public MigrationPermissionsEx() {
super("permissionsex", "Migration from PermissionsEx", "/%s migration permissionsex [world names]",
Permission.MIGRATION, Predicate.alwaysFalse());
@@ -92,7 +63,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
return CommandResult.STATE_ERROR;
}
if (plugin.getType() != LuckPermsPlugin.Type.BUKKIT) {
if (plugin.getType() != PlatformType.BUKKIT) {
// Sponge uses a completely different version of PEX.
log.severe("PEX import is not supported on this platform.");
return CommandResult.STATE_ERROR;
@@ -123,9 +94,15 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
// Migrate all groups.
log.info("PermissionsEx Migration: Starting group migration.");
int maxGroupWeight = 0;
int groupCount = 0;
for (PermissionGroup group : manager.getGroupList()) {
int groupWeight = group.getWeight() * -1;
groupCount ++;
maxGroupWeight = Math.max(maxGroupWeight, groupWeight);
final String name = group.getName().toLowerCase();
plugin.getDatastore().createAndLoadGroup(name);
Group lpGroup = plugin.getGroupManager().get(name);
@@ -224,10 +201,10 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
if (prefix != null && !prefix.equals("")) {
prefix = ArgumentChecker.escapeCharacters(prefix);
try {
lpGroup.setPermission("prefix.50." + prefix, true);
lpGroup.setPermission("prefix." + groupWeight + "." + prefix, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(lpGroup).action("set prefix.50." + prefix + " true")
.acted(lpGroup).action("set prefix." + groupWeight + "." + prefix + " true")
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
@@ -239,10 +216,10 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
if (suffix != null && !suffix.equals("")) {
suffix = ArgumentChecker.escapeCharacters(suffix);
try {
lpGroup.setPermission("suffix.50." + suffix, true);
lpGroup.setPermission("suffix." + groupWeight + "." + suffix, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(lpGroup).action("set suffix.50." + suffix + " true")
.acted(lpGroup).action("set suffix." + groupWeight + "." + suffix + " true")
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
@@ -251,6 +228,8 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
}
}
plugin.getDatastore().saveGroup(lpGroup);
}
log.info("PermissionsEx Migration: Migrated " + groupCount + " groups");
@@ -258,25 +237,16 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
// Migrate all users
log.info("PermissionsEx Migration: Starting user migration.");
int userCount = 0;
maxGroupWeight++;
for (PermissionUser user : manager.getUsers()) {
UUID u = null;
try {
u = UUID.fromString(user.getIdentifier());
} catch (IllegalArgumentException e) {
u = ni.nameToUUID(user.getIdentifier());
if (u == null) {
if (bukkitPlayer != null) {
try {
Object playerObj = getPlayerMethod.invoke(user);
u = (UUID) getUniqueIdMethod.invoke(playerObj);
} catch (Throwable t) {
t.printStackTrace();
}
}
u = plugin.getUUID(user.getIdentifier());
}
}
@@ -375,10 +345,10 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
if (prefix != null && !prefix.equals("")) {
prefix = ArgumentChecker.escapeCharacters(prefix);
try {
lpUser.setPermission("prefix.100." + prefix, true);
lpUser.setPermission("prefix." + maxGroupWeight + "." + prefix, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(lpUser).action("set prefix.100." + prefix + " true")
.acted(lpUser).action("set prefix." + maxGroupWeight + "." + prefix + " true")
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
@@ -390,10 +360,10 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
if (suffix != null && !suffix.equals("")) {
suffix = ArgumentChecker.escapeCharacters(suffix);
try {
lpUser.setPermission("suffix.100." + suffix, true);
lpUser.setPermission("suffix." + maxGroupWeight + "." + suffix, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(lpUser).action("set suffix.100." + suffix + " true")
.acted(lpUser).action("set suffix." + maxGroupWeight + "." + suffix + " true")
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
@@ -402,6 +372,22 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
}
}
String primary = null;
int weight = -100;
for (PermissionGroup group : user.getOwnParents()) {
if (group.getRank() > weight) {
primary = group.getName();
weight = group.getWeight();
}
}
if (primary != null) {
try {
lpUser.setPermission("group." + primary.toLowerCase(), true);
} catch (ObjectAlreadyHasException ignored) {}
lpUser.setPrimaryGroup(primary);
}
plugin.getUserManager().cleanup(lpUser);
plugin.getDatastore().saveUser(lpUser);
}
@@ -57,6 +57,15 @@ public class UserMainCommand extends MainCommand<User> {
.add(new UserPromote())
.add(new UserDemote())
.add(new UserShowPos())
.add(new UserChatMeta())
.add(new UserAddPrefix())
.add(new UserAddSuffix())
.add(new UserRemovePrefix())
.add(new UserRemoveSuffix())
.add(new UserAddTempPrefix())
.add(new UserAddTempSuffix())
.add(new UserRemoveTempPrefix())
.add(new UserRemoveTempSuffix())
.add(new UserClear())
.build()
);
@@ -84,7 +93,10 @@ public class UserMainCommand extends MainCommand<User> {
}
}
if (!plugin.getDatastore().loadUser(u, "null")) {
String name = plugin.getDatastore().getName(u);
if (name == null) name = "null";
if (!plugin.getDatastore().loadUser(u, name)) {
Message.LOADING_ERROR.send(sender);
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.user.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class UserAddPrefix extends SubCommand<User> {
public UserAddPrefix() {
super("addprefix", "Adds a prefix to the user", "/%s user <user> addprefix <priority> <prefix> [server] [world]",
Permission.USER_ADDPREFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
user.setPermission(node, true, server);
Message.ADDPREFIX_SERVER_SUCCESS.send(sender, user.getName(), prefix, priority, server);
LogEntry.build().actor(sender).acted(user)
.action("addprefix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
user.setPermission(node, true, server, world);
Message.ADDPREFIX_SERVER_WORLD_SUCCESS.send(sender, user.getName(), prefix, priority, server, world);
LogEntry.build().actor(sender).acted(user)
.action("addprefix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
user.setPermission(node, true);
Message.ADDPREFIX_SUCCESS.send(sender, user.getName(), prefix, priority);
LogEntry.build().actor(sender).acted(user)
.action("addprefix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(user, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_PREFIX.send(sender, user.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.user.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class UserAddSuffix extends SubCommand<User> {
public UserAddSuffix() {
super("addsuffix", "Adds a suffix to the user", "/%s user <user> addsuffix <priority> <suffix> [server] [world]",
Permission.USER_ADDSUFFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
user.setPermission(node, true, server);
Message.ADDSUFFIX_SERVER_SUCCESS.send(sender, user.getName(), suffix, priority, server);
LogEntry.build().actor(sender).acted(user)
.action("addsuffix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
user.setPermission(node, true, server, world);
Message.ADDSUFFIX_SERVER_WORLD_SUCCESS.send(sender, user.getName(), suffix, priority, server, world);
LogEntry.build().actor(sender).acted(user)
.action("addsuffix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
user.setPermission(node, true);
Message.ADDSUFFIX_SUCCESS.send(sender, user.getName(), suffix, priority);
LogEntry.build().actor(sender).acted(user)
.action("addsuffix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(user, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_SUFFIX.send(sender, user.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.user.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.ArgumentChecker;
import me.lucko.luckperms.utils.DateUtil;
import java.util.List;
public class UserAddTempPrefix extends SubCommand<User> {
public UserAddTempPrefix() {
super("addtempprefix", "Adds a prefix to the user temporarily", "/%s user <user> addtempprefix <priority> <prefix> <duration> [server] [world]",
Permission.USER_ADD_TEMP_PREFIX, Predicate.notInRange(3, 5));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
long duration;
try {
duration = Long.parseLong(args.get(2));
} catch (NumberFormatException e) {
try {
duration = DateUtil.parseDateDiff(args.get(2), true);
} catch (DateUtil.IllegalDateException e1) {
Message.ILLEGAL_DATE_ERROR.send(sender, args.get(2));
return CommandResult.INVALID_ARGS;
}
}
if (DateUtil.shouldExpire(duration)) {
Message.PAST_DATE_ERROR.send(sender);
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 4) {
final String server = args.get(3).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 4) {
user.setPermission(node, true, server, duration);
Message.ADD_TEMP_PREFIX_SERVER_SUCCESS.send(sender, user.getName(), prefix, priority, server, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(user)
.action("addtempprefix " + priority + " " + args.get(1) + " " + duration + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(4).toLowerCase();
user.setPermission(node, true, server, world, duration);
Message.ADD_TEMP_PREFIX_SERVER_WORLD_SUCCESS.send(sender, user.getName(), prefix, priority, server, world, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(user)
.action("addtempprefix " + priority + " " + args.get(1) + " " + duration + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
user.setPermission(node, true, duration);
Message.ADD_TEMP_PREFIX_SUCCESS.send(sender, user.getName(), prefix, priority, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(user)
.action("addtempprefix " + priority + " " + args.get(1) + " " + duration)
.build().submit(plugin, sender);
}
save(user, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_PREFIX.send(sender, user.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.user.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.ArgumentChecker;
import me.lucko.luckperms.utils.DateUtil;
import java.util.List;
public class UserAddTempSuffix extends SubCommand<User> {
public UserAddTempSuffix() {
super("addtempsuffix", "Adds a suffix to the user temporarily", "/%s user <user> addtempsuffix <priority> <suffix> <duration> [server] [world]",
Permission.USER_ADD_TEMP_SUFFIX, Predicate.notInRange(3, 5));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
long duration;
try {
duration = Long.parseLong(args.get(2));
} catch (NumberFormatException e) {
try {
duration = DateUtil.parseDateDiff(args.get(2), true);
} catch (DateUtil.IllegalDateException e1) {
Message.ILLEGAL_DATE_ERROR.send(sender, args.get(2));
return CommandResult.INVALID_ARGS;
}
}
if (DateUtil.shouldExpire(duration)) {
Message.PAST_DATE_ERROR.send(sender);
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 4) {
final String server = args.get(3).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 4) {
user.setPermission(node, true, server, duration);
Message.ADD_TEMP_SUFFIX_SERVER_SUCCESS.send(sender, user.getName(), suffix, priority, server, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(user)
.action("addtempsuffix " + priority + " " + args.get(1) + " " + duration + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(4).toLowerCase();
user.setPermission(node, true, server, world, duration);
Message.ADD_TEMP_SUFFIX_SERVER_WORLD_SUCCESS.send(sender, user.getName(), suffix, priority, server, world, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(user)
.action("addtempsuffix " + priority + " " + args.get(1) + " " + duration + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
user.setPermission(node, true, duration);
Message.ADD_TEMP_SUFFIX_SUCCESS.send(sender, user.getName(), suffix, priority, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(user)
.action("addtempsuffix " + priority + " " + args.get(1) + " " + duration)
.build().submit(plugin, sender);
}
save(user, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_SUFFIX.send(sender, user.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.user.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.users.User;
import java.util.AbstractMap.SimpleEntry;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
public class UserChatMeta extends SubCommand<User> {
public UserChatMeta() {
super("chatmeta", "Displays a users chat meta", "/%s user <user> chatmeta", Permission.USER_CHATMETA,
Predicate.alwaysFalse());
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
SortedSet<Map.Entry<Integer, String>> prefixes = new TreeSet<>(Util.getMetaComparator().reversed());
SortedSet<Map.Entry<Integer, String>> suffixes = new TreeSet<>(Util.getMetaComparator().reversed());
for (Node node : user.getAllNodes(null)) {
if (!node.isSuffix() && !node.isPrefix()) {
continue;
}
StringBuilder sb = new StringBuilder();
if (node.isServerSpecific()) {
if (node.isWorldSpecific()) {
sb.append("&6W=").append(node.getWorld().get()).append(" ");
}
sb.append("&6S=").append(node.getServer().get()).append(" ");
}
if (node.isPrefix()) {
sb.append(node.getPrefix().getValue());
prefixes.add(new SimpleEntry<>(node.getPrefix().getKey(), sb.toString()));
}
if (node.isSuffix()) {
sb.append(node.getSuffix().getValue());
suffixes.add(new SimpleEntry<>(node.getSuffix().getKey(), sb.toString()));
}
}
if (prefixes.isEmpty()) {
Message.CHAT_META_PREFIX_NONE.send(sender, user.getName());
} else {
Message.CHAT_META_PREFIX_HEADER.send(sender, user.getName());
for (Map.Entry<Integer, String> e : prefixes) {
Message.CHAT_META_ENTRY.send(sender, e.getKey(), e.getValue());
}
}
if (suffixes.isEmpty()) {
Message.CHAT_META_SUFFIX_NONE.send(sender, user.getName());
} else {
Message.CHAT_META_SUFFIX_HEADER.send(sender, user.getName());
for (Map.Entry<Integer, String> e : suffixes) {
Message.CHAT_META_ENTRY.send(sender, e.getKey(), e.getValue());
}
}
return CommandResult.SUCCESS;
}
}
@@ -30,8 +30,6 @@ import me.lucko.luckperms.users.User;
import java.util.List;
import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
public class UserListNodes extends SubCommand<User> {
public UserListNodes() {
super("listnodes", "Lists the permission nodes the user has", "/%s user <user> listnodes",
@@ -40,8 +38,8 @@ public class UserListNodes extends SubCommand<User> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
Message.LISTNODES.send(sender, user.getName(), Util.permNodesToString(exportToLegacy(user.getPermanentNodes())));
Message.LISTNODES_TEMP.send(sender, user.getName(), Util.tempNodesToString(user.getTemporaryNodesLegacy()));
Message.LISTNODES.send(sender, user.getName(), Util.permNodesToString(user.getPermissions(false)));
Message.LISTNODES_TEMP.send(sender, user.getName(), Util.tempNodesToString(user.getPermissions(false)));
return CommandResult.SUCCESS;
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.user.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class UserRemovePrefix extends SubCommand<User> {
public UserRemovePrefix() {
super("removeprefix", "Removes a prefix from a user", "/%s user <user> removeprefix <priority> <prefix> [server] [world]",
Permission.USER_REMOVEPREFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
user.unsetPermission(node, server);
Message.REMOVEPREFIX_SERVER_SUCCESS.send(sender, user.getName(), prefix, priority, server);
LogEntry.build().actor(sender).acted(user)
.action("removeprefix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
user.unsetPermission(node, server, world);
Message.REMOVEPREFIX_SERVER_WORLD_SUCCESS.send(sender, user.getName(), prefix, priority, server, world);
LogEntry.build().actor(sender).acted(user)
.action("removeprefix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
user.unsetPermission(node);
Message.REMOVEPREFIX_SUCCESS.send(sender, user.getName(), prefix, priority);
LogEntry.build().actor(sender).acted(user)
.action("removeprefix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(user, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectLacksException e) {
Message.DOES_NOT_HAVE_PREFIX.send(sender, user.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.user.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class UserRemoveSuffix extends SubCommand<User> {
public UserRemoveSuffix() {
super("removesuffix", "Removes a suffix from a user", "/%s user <user> removesuffix <priority> <suffix> [server] [world]",
Permission.USER_REMOVESUFFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
user.unsetPermission(node, server);
Message.REMOVESUFFIX_SERVER_SUCCESS.send(sender, user.getName(), suffix, priority, server);
LogEntry.build().actor(sender).acted(user)
.action("removesuffix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
user.unsetPermission(node, server, world);
Message.REMOVESUFFIX_SERVER_WORLD_SUCCESS.send(sender, user.getName(), suffix, priority, server, world);
LogEntry.build().actor(sender).acted(user)
.action("removesuffix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
user.unsetPermission(node);
Message.REMOVESUFFIX_SUCCESS.send(sender, user.getName(), suffix, priority);
LogEntry.build().actor(sender).acted(user)
.action("removesuffix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(user, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectLacksException e) {
Message.DOES_NOT_HAVE_SUFFIX.send(sender, user.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.user.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class UserRemoveTempPrefix extends SubCommand<User> {
public UserRemoveTempPrefix() {
super("removetempprefix", "Removes a temporary prefix from a user", "/%s user <user> removetempprefix <priority> <prefix> [server] [world]",
Permission.USER_REMOVE_TEMP_PREFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
user.unsetPermission(node, server, true);
Message.REMOVE_TEMP_PREFIX_SERVER_SUCCESS.send(sender, user.getName(), prefix, priority, server);
LogEntry.build().actor(sender).acted(user)
.action("removetempprefix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
user.unsetPermission(node, server, world, true);
Message.REMOVE_TEMP_PREFIX_SERVER_WORLD_SUCCESS.send(sender, user.getName(), prefix, priority, server, world);
LogEntry.build().actor(sender).acted(user)
.action("removetempprefix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
user.unsetPermission(node, true);
Message.REMOVE_TEMP_PREFIX_SUCCESS.send(sender, user.getName(), prefix, priority);
LogEntry.build().actor(sender).acted(user)
.action("removetempprefix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(user, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectLacksException e) {
Message.DOES_NOT_HAVE_PREFIX.send(sender, user.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.user.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class UserRemoveTempSuffix extends SubCommand<User> {
public UserRemoveTempSuffix() {
super("removetempsuffix", "Removes a temporary suffix from a user", "/%s user <user> removetempsuffix <priority> <suffix> [server] [world]",
Permission.USER_REMOVE_TEMP_SUFFIX, Predicate.notInRange(2, 4));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
user.unsetPermission(node, server, true);
Message.REMOVE_TEMP_SUFFIX_SERVER_SUCCESS.send(sender, user.getName(), suffix, priority, server);
LogEntry.build().actor(sender).acted(user)
.action("removetempsuffix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
user.unsetPermission(node, server, world, true);
Message.REMOVE_TEMP_SUFFIX_SERVER_WORLD_SUCCESS.send(sender, user.getName(), suffix, priority, server, world);
LogEntry.build().actor(sender).acted(user)
.action("removetempsuffix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
user.unsetPermission(node, true);
Message.REMOVE_TEMP_SUFFIX_SUCCESS.send(sender, user.getName(), suffix, priority);
LogEntry.build().actor(sender).acted(user)
.action("removetempsuffix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(user, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectLacksException e) {
Message.DOES_NOT_HAVE_SUFFIX.send(sender, user.getName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -30,6 +30,7 @@ import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User;
@@ -55,8 +56,10 @@ public class UserSetPrimaryGroup extends SubCommand<User> {
}
if (!user.isInGroup(group)) {
Message.USER_PRIMARYGROUP_ERROR_NOTMEMBER.send(sender, label);
return CommandResult.STATE_ERROR;
Message.USER_PRIMARYGROUP_ERROR_NOTMEMBER.send(sender, user.getName(), group.getName());
try {
user.addGroup(group);
} catch (ObjectAlreadyHasException ignored) {}
}
user.setPrimaryGroup(group.getName());
@@ -40,6 +40,7 @@ public enum Message {
PLAYER_ONLINE("&aOnline", false),
PLAYER_OFFLINE("&cOffline", false),
LOADING_ERROR("Permissions data could not be loaded. Please contact an administrator.", true),
OP_DISABLED("&eThe vanilla OP system is disabled on this server.", false),
LOG("&3LOG &3&l> %s", true),
COMMAND_NOT_RECOGNISED("Command not recognised.", true),
@@ -168,6 +169,46 @@ public enum Message {
ILLEGAL_DATE_ERROR("Could not parse date '%s'.", true),
PAST_DATE_ERROR("You cannot set a date in the past!", true),
CHAT_META_PREFIX_HEADER("&e%s's Prefixes", true),
CHAT_META_SUFFIX_HEADER("&e%s's Suffixes", true),
CHAT_META_ENTRY("&d-> &e%s &6- &f\"%s&f\"", true),
CHAT_META_PREFIX_NONE("&e%s has no prefixes.", true),
CHAT_META_SUFFIX_NONE("&e%s has no suffixes.", true),
META_INVALID_PRIORITY("Invalid priority '%s'. Expected a number.", true),
ALREADY_HAS_PREFIX("%s already has that prefix set.", true),
ALREADY_HAS_SUFFIX("%s already has that suffix set.", true),
DOES_NOT_HAVE_PREFIX("%s doesn't have that prefix set.", true),
DOES_NOT_HAVE_SUFFIX("%s doesn't have that suffix set.", true),
ADDPREFIX_SUCCESS("&b%s&a had prefix &f\"%s&f\"&a set at a priority of &b%s&a.", true),
ADDPREFIX_SERVER_SUCCESS("&b%s&a had prefix &f\"%s&f\"&a set at a priority of &b%s&a on server &b%s&a.", true),
ADDPREFIX_SERVER_WORLD_SUCCESS("&b%s&a had prefix &f\"%s&f\"&a set at a priority of &b%s&a on server &b%s&a, world &b%s&a.", true),
REMOVEPREFIX_SUCCESS("&b%s&a had prefix &f\"%s&f\"&a at priority &b%s&a removed.", true),
REMOVEPREFIX_SERVER_SUCCESS("&b%s&a had prefix &f\"%s&f\"&a at priority &b%s&a removed on server &b%s&a.", true),
REMOVEPREFIX_SERVER_WORLD_SUCCESS("&b%s&a had prefix &f\"%s&f\"&a at priority &b%s&a removed on server &b%s&a, world &b%s&a.", true),
ADDSUFFIX_SUCCESS("&b%s&a had suffix &f\"%s&f\"&a set at a priority of &b%s&a.", true),
ADDSUFFIX_SERVER_SUCCESS("&b%s&a had suffix &f\"%s&f\"&a set at a priority of &b%s&a on server &b%s&a.", true),
ADDSUFFIX_SERVER_WORLD_SUCCESS("&b%s&a had suffix &f\"%s&f\"&a set at a priority of &b%s&a on server &b%s&a, world &b%s&a.", true),
REMOVESUFFIX_SUCCESS("&b%s&a had suffix &f\"%s&f\"&a at priority &b%s&a removed.", true),
REMOVESUFFIX_SERVER_SUCCESS("&b%s&a had suffix &f\"%s&f\"&a at priority &b%s&a removed on server &b%s&a.", true),
REMOVESUFFIX_SERVER_WORLD_SUCCESS("&b%s&a had suffix &f\"%s&f\"&a at priority &b%s&a removed on server &b%s&a, world &b%s&a.", true),
ADD_TEMP_PREFIX_SUCCESS("&b%s&a had prefix &f\"%s&f\"&a set at a priority of &b%s&a for a duration of &b%s&a.", true),
ADD_TEMP_PREFIX_SERVER_SUCCESS("&b%s&a had prefix &f\"%s&f\"&a set at a priority of &b%s&a on server &b%s&a, for a duration of &b%s&a.", true),
ADD_TEMP_PREFIX_SERVER_WORLD_SUCCESS("&b%s&a had prefix &f\"%s&f\"&a set at a priority of &b%s&a on server &b%s&a, world &b%s&a, for a duration of &b%s&a.", true),
REMOVE_TEMP_PREFIX_SUCCESS("&b%s&a had temporary prefix &f\"%s&f\"&a at priority &b%s&a removed.", true),
REMOVE_TEMP_PREFIX_SERVER_SUCCESS("&b%s&a had temporary prefix &f\"%s&f\"&a at priority &b%s&a removed on server &b%s&a.", true),
REMOVE_TEMP_PREFIX_SERVER_WORLD_SUCCESS("&b%s&a had temporary prefix &f\"%s&f\"&a at priority &b%s&a removed on server &b%s&a, world &b%s&a.", true),
ADD_TEMP_SUFFIX_SUCCESS("&b%s&a had suffix &f\"%s&f\"&a set at a priority of &b%s&a for a duration of &b%s&a.", true),
ADD_TEMP_SUFFIX_SERVER_SUCCESS("&b%s&a had suffix &f\"%s&f\"&a set at a priority of &b%s&a on server &b%s&a, for a duration of &b%s&a.", true),
ADD_TEMP_SUFFIX_SERVER_WORLD_SUCCESS("&b%s&a had suffix &f\"%s&f\"&a set at a priority of &b%s&a on server &b%s&a, world &b%s&a, for a duration of &b%s&a.", true),
REMOVE_TEMP_SUFFIX_SUCCESS("&b%s&a had temporary suffix &f\"%s&f\"&a at priority &b%s&a removed.", true),
REMOVE_TEMP_SUFFIX_SERVER_SUCCESS("&b%s&a had temporary suffix &f\"%s&f\"&a at priority &b%s&a removed on server &b%s&a.", true),
REMOVE_TEMP_SUFFIX_SERVER_WORLD_SUCCESS("&b%s&a had temporary suffix &f\"%s&f\"&a at priority &b%s&a removed on server &b%s&a, world &b%s&a.", true),
USER_INFO(
PREFIX + "&d-> &eUser: &6%s" + "\n" +
PREFIX + "&d-> &eUUID: &6%s" + "\n" +
@@ -195,7 +236,7 @@ public enum Message {
USER_REMOVEGROUP_ERROR_PRIMARY("You cannot remove a user from their primary group.", true),
USER_PRIMARYGROUP_SUCCESS("&b%s&a's primary group was set to &b%s&a.", true),
USER_PRIMARYGROUP_ERROR_ALREADYHAS("The user already has this group set as their primary group.", true),
USER_PRIMARYGROUP_ERROR_NOTMEMBER("The user must be a member of the group first! Use &4/%s user <user> addgroup <group>", true),
USER_PRIMARYGROUP_ERROR_NOTMEMBER("&b%s&a was not already a member of &b%s&a, adding them now.", true),
USER_SHOWTRACKS_INFO("&aShowing tracks that contain the group '&b%s&a' (%s's primary group)", true),
USER_PROMOTE_SUCCESS_PROMOTE("&aPromoting user along track &b%s&a from &b%s&a to &b%s&a.", true),
USER_PROMOTE_SUCCESS_REMOVE("&b%s&a was removed from &b%s&a, added to &b%s&a, and their primary group was set to &b%s&a.", true),
@@ -40,7 +40,7 @@ public class Patterns {
public static final Pattern DOT = Pattern.compile("\\.");
public static final Pattern VERTICAL_BAR = Pattern.compile("\\|");
public static final Pattern GROUP_MATCH = Pattern.compile("group\\..*");
public static final Pattern NON_ALPHA_NUMERIC = Pattern.compile("[^A-Za-z0-9]");
public static final Pattern NON_ALPHA_NUMERIC = Pattern.compile("[\\/\\$\\.\\-]");
public static final Pattern NON_USERNAME = Pattern.compile("[^A-Za-z0-9_]");
public static final Pattern SHORTHAND_NODE = Pattern.compile("\\.\\([^.]+\\)");
public static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + String.valueOf('§') + "[0-9A-FK-OR]");
@@ -23,9 +23,11 @@
package me.lucko.luckperms.constants;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.commands.Sender;
@SuppressWarnings("SpellCheckingInspection")
@Getter
@AllArgsConstructor
public enum Permission {
@@ -60,6 +62,15 @@ public enum Permission {
USER_PROMOTE("promote", "user"),
USER_DEMOTE("demote", "user"),
USER_SHOWPOS("showpos", "user"),
USER_CHATMETA("chatmeta", "user"),
USER_ADDPREFIX("addprefix", "user"),
USER_REMOVEPREFIX("removeprefix", "user"),
USER_ADDSUFFIX("addsuffix", "user"),
USER_REMOVESUFFIX("removesuffix", "user"),
USER_ADD_TEMP_PREFIX("addtempprefix", "user"),
USER_REMOVE_TEMP_PREFIX("removetempprefix", "user"),
USER_ADD_TEMP_SUFFIX("addtempsuffix", "user"),
USER_REMOVE_TEMP_SUFFIX("removetempsuffix", "user"),
USER_CLEAR("clear", "user"),
GROUP_INFO("info", "group"),
@@ -75,6 +86,15 @@ public enum Permission {
GROUP_SET_TEMP_INHERIT("settempinherit", "group"),
GROUP_UNSET_TEMP_INHERIT("unsettempinherit", "group"),
GROUP_SHOWTRACKS("showtracks", "group"),
GROUP_CHATMETA("chatmeta", "group"),
GROUP_ADDPREFIX("addprefix", "group"),
GROUP_REMOVEPREFIX("removeprefix", "group"),
GROUP_ADDSUFFIX("addsuffix", "group"),
GROUP_REMOVESUFFIX("removesuffix", "group"),
GROUP_ADD_TEMP_PREFIX("addtempprefix", "group"),
GROUP_REMOVE_TEMP_PREFIX("removetempprefix", "group"),
GROUP_ADD_TEMP_SUFFIX("addtempsuffix", "group"),
GROUP_REMOVE_TEMP_SUFFIX("removetempsuffix", "group"),
GROUP_CLEAR("clear", "group"),
GROUP_RENAME("rename", "group"),
@@ -95,15 +115,15 @@ public enum Permission {
MIGRATION("migration", null);
private String node;
private String tag;
private String group;
public String getNode() {
if (group != null) {
return "luckperms." + group + "." + node;
return "luckperms." + group + "." + tag;
}
return "luckperms." + node;
return "luckperms." + tag;
}
public boolean isAuthorized(Sender sender) {
@@ -28,6 +28,9 @@ import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.storage.DatastoreConfiguration;
import java.util.HashMap;
import java.util.Map;
public abstract class LPConfiguration<T extends LuckPermsPlugin> {
@Getter(AccessLevel.PROTECTED)
@@ -67,17 +70,19 @@ public abstract class LPConfiguration<T extends LuckPermsPlugin> {
}
/**
* As of 1.6, this value is a constant
* As of 2.6, this value is a constant
* @return the default group node
*/
@SuppressWarnings("SameReturnValue")
public String getDefaultGroupNode() {
return "group.default";
}
/**
* As of 1.6, this value is a constant
* As of 2.6, this value is a constant
* @return the name of the default group
*/
@SuppressWarnings("SameReturnValue")
public String getDefaultGroupName() {
return "default";
}
@@ -102,6 +107,26 @@ public abstract class LPConfiguration<T extends LuckPermsPlugin> {
return getBoolean("apply-shorthand", true);
}
public boolean getLogNotify() {
return getBoolean("log-notify", true);
}
public boolean getEnableOps() {
return getBoolean("enable-ops", true);
}
public boolean getCommandsAllowOp() {
return getBoolean("commands-allow-op", true);
}
public String getVaultServer() {
return getString("vault-server", "global");
}
public boolean getVaultIncludeGlobal() {
return getBoolean("vault-include-global", true);
}
public DatastoreConfiguration getDatabaseValues() {
return new DatastoreConfiguration(
getString("data.address", null),
@@ -114,4 +139,19 @@ public abstract class LPConfiguration<T extends LuckPermsPlugin> {
public String getStorageMethod() {
return getString("storage-method", defaultStorage);
}
public boolean getSplitStorage() {
return getBoolean("split-storage.enabled", false);
}
public Map<String, String> getSplitStorageOptions() {
Map<String, String> map = new HashMap<>();
map.put("user", getString("split-storage.methods.user", defaultStorage));
map.put("group", getString("split-storage.methods.group", defaultStorage));
map.put("track", getString("split-storage.methods.track", defaultStorage));
map.put("uuid", getString("split-storage.methods.uuid", defaultStorage));
map.put("log", getString("split-storage.methods.log", defaultStorage));
return map;
}
}
@@ -39,7 +39,6 @@ import me.lucko.luckperms.groups.Group;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.stream.Collectors;
/**
@@ -78,20 +77,26 @@ public abstract class PermissionHolder {
* Returns a Set of nodes in priority order
* @return the holders transient and permanent nodes
*/
public SortedSet<Node> getPermissions() {
public SortedSet<Node> getPermissions(boolean mergeTemp) {
// Returns no duplicate nodes. as in, nodes with the same value.
TreeSet<Node> combined = new TreeSet<>(PRIORITY_COMPARATOR);
TreeSet<Node> combined = new TreeSet<>(PriorityComparator.reverse());
combined.addAll(nodes);
combined.addAll(transientNodes);
TreeSet<Node> permissions = new TreeSet<>(PRIORITY_COMPARATOR);
TreeSet<Node> permissions = new TreeSet<>(PriorityComparator.reverse());
combined:
for (Node node : combined) {
for (Node other : permissions) {
if (node.equalsIgnoringValue(other)) {
continue combined;
if (mergeTemp) {
if (node.equalsIgnoringValueOrTemp(other)) {
continue combined;
}
} else {
if (node.almostEquals(other)) {
continue combined;
}
}
}
@@ -139,8 +144,7 @@ public abstract class PermissionHolder {
* @return a set of nodes
*/
public SortedSet<Node> getAllNodes(List<String> excludedGroups) {
SortedSet<Node> all = new ConcurrentSkipListSet<>(PRIORITY_COMPARATOR);
all.addAll(getPermissions());
SortedSet<Node> all = getPermissions(true);
if (excludedGroups == null) {
excludedGroups = new ArrayList<>();
@@ -148,7 +152,7 @@ public abstract class PermissionHolder {
excludedGroups.add(getObjectName().toLowerCase());
Set<String> parents = getPermissions().stream()
Set<String> parents = getPermissions(true).stream()
.filter(Node::isGroupNode)
.map(Node::getGroupName)
.collect(Collectors.toSet());
@@ -192,11 +196,12 @@ public abstract class PermissionHolder {
SortedSet<Node> allNodes;
if (applyGroups) {
allNodes = sort(getAllNodes(null), true);
allNodes = getAllNodes(null);
} else {
allNodes = sort(getPermissions(), true);
allNodes = getPermissions(true);
}
all:
for (Node node : allNodes) {
if (!node.shouldApplyOnServer(server, includeGlobal, plugin.getConfiguration().getApplyRegex())) {
continue;
@@ -210,6 +215,13 @@ public abstract class PermissionHolder {
continue;
}
// Force higher priority nodes to override
for (Node alreadyIn : perms) {
if (node.getPermission().equals(alreadyIn.getPermission())) {
continue all;
}
}
perms.add(node);
}
@@ -230,6 +242,13 @@ public abstract class PermissionHolder {
Map<String, Boolean> perms = new HashMap<>();
for (Node node : getAllNodesFiltered(server, world, extraContext, includeGlobal, applyGroups)) {
if (node.getPermission().equals("*") || node.getPermission().equals("'*'")) {
if (possibleNodes != null && plugin.getConfiguration().getApplyWildcards()) {
possibleNodes.forEach(n -> perms.put(n, true));
continue;
}
}
perms.put(node.getPermission(), node.getValue());
if (plugin.getConfiguration().getApplyShorthand()) {
@@ -286,7 +305,7 @@ public abstract class PermissionHolder {
private static Tristate hasPermission(Set<Node> toQuery, Node node) {
for (Node n : toQuery) {
if (n.equalsIgnoringValue(node)) {
if (n.almostEquals(node)) {
return n.getTristate();
}
}
@@ -423,11 +442,17 @@ public abstract class PermissionHolder {
* @throws ObjectLacksException if the holder doesn't have this node already
*/
public void unsetPermission(Node node) throws ObjectLacksException {
if (hasPermission(node, false) == Tristate.FALSE) {
if (hasPermission(node, false) != Tristate.TRUE) {
throw new ObjectLacksException();
}
nodes.remove(node);
Iterator<Node> iterator = nodes.iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (entry.almostEquals(node)) {
iterator.remove();
}
}
if (node.isGroupNode()) {
plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent(new PermissionHolderLink(this),
@@ -447,7 +472,13 @@ public abstract class PermissionHolder {
throw new ObjectLacksException();
}
transientNodes.remove(node);
Iterator<Node> iterator = transientNodes.iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (entry.almostEquals(node)) {
iterator.remove();
}
}
if (node.isGroupNode()) {
plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent(new PermissionHolderLink(this),
@@ -485,7 +516,7 @@ public abstract class PermissionHolder {
* @return The temporary nodes held by the holder
*/
public Set<Node> getTemporaryNodes() {
return getPermissions().stream().filter(Node::isTemporary).collect(Collectors.toSet());
return getPermissions(false).stream().filter(Node::isTemporary).collect(Collectors.toSet());
}
@Deprecated
@@ -503,7 +534,7 @@ public abstract class PermissionHolder {
* @return The permanent nodes held by the holder
*/
public Set<Node> getPermanentNodes() {
return getPermissions().stream().filter(Node::isPermanent).collect(Collectors.toSet());
return getPermissions(false).stream().filter(Node::isPermanent).collect(Collectors.toSet());
}
/*
@@ -524,47 +555,4 @@ public abstract class PermissionHolder {
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups) {
return getLocalPermissions(server, null, excludedGroups, null);
}
public static SortedSet<Node> sort(Set<Node> toSort, boolean reversed) {
TreeSet<Node> set = new TreeSet<>(reversed ? PRIORITY_COMPARATOR.reversed() : PRIORITY_COMPARATOR);
set.addAll(toSort);
return set;
}
private static final PriorityComparator PRIORITY_COMPARATOR = new PriorityComparator();
private static class PriorityComparator implements Comparator<Node> {
@Override
public int compare(Node o1, Node o2) {
if (o1.isOverride() != o2.isOverride()) {
return o1.isOverride() ? 1 : -1;
}
if (o1.isServerSpecific() != o2.isServerSpecific()) {
return o1.isServerSpecific() ? 1 : -1;
}
if (o1.isWorldSpecific() != o2.isWorldSpecific()) {
return o1.isWorldSpecific() ? 1 : -1;
}
if (o1.isTemporary() != o2.isTemporary()) {
return o1.isTemporary() ? 1 : -1;
}
if (o1.isWildcard() != o2.isWildcard()) {
return o1.isWildcard() ? 1 : -1;
}
if (o1.isTemporary()) {
return o1.getSecondsTilExpiry() < o2.getSecondsTilExpiry() ? 1 : -1;
}
if (o1.isWildcard()) {
return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1;
}
return 1;
}
}
}
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.core;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import me.lucko.luckperms.api.Node;
import java.util.Comparator;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PriorityComparator implements Comparator<Node> {
private static final PriorityComparator instance = new PriorityComparator();
public static Comparator<Node> get() {
return instance;
}
public static Comparator<Node> reverse() {
return instance.reversed();
}
@Override
public int compare(Node o1, Node o2) {
if (o1.equals(o2)) {
return 0;
}
if (o1.isOverride() != o2.isOverride()) {
return o1.isOverride() ? 1 : -1;
}
if (o1.isServerSpecific() != o2.isServerSpecific()) {
return o1.isServerSpecific() ? 1 : -1;
}
if (o1.isWorldSpecific() != o2.isWorldSpecific()) {
return o1.isWorldSpecific() ? 1 : -1;
}
if (o1.isTemporary() != o2.isTemporary()) {
return o1.isTemporary() ? 1 : -1;
}
if (o1.isWildcard() != o2.isWildcard()) {
return o1.isWildcard() ? 1 : -1;
}
if (o1.isTemporary()) {
return o1.getSecondsTilExpiry() < o2.getSecondsTilExpiry() ? 1 : -1;
}
if (o1.isWildcard()) {
return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1;
}
return 1;
}
}
@@ -30,7 +30,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* @see me.lucko.luckperms.api.UuidCache
* @see me.lucko.luckperms.api.UuidCache for docs
*/
public class UuidCache {
@@ -59,7 +59,6 @@ public class Importer {
running = true;
return true;
}
public void start(Sender executor, List<String> commands) {
@@ -52,6 +52,7 @@ public class LogEntry extends me.lucko.luckperms.api.LogEntry {
plugin.getDatastore().logAction(this);
LogNotifyEvent event = new LogNotifyEvent(this);
event.setCancelled(!plugin.getConfiguration().getLogNotify());
plugin.getApiProvider().fireEvent(event);
if (event.isCancelled()) return;
@@ -26,17 +26,16 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.event.events.GroupAddEvent;
import me.lucko.luckperms.api.implementation.internal.GroupLink;
import me.lucko.luckperms.api.implementation.internal.PermissionHolderLink;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.utils.Identifiable;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ToString(of = {"name"})
@@ -321,10 +320,11 @@ public class Group extends PermissionHolder implements Identifiable<String> {
*/
private List<String> getGroups(String server, String world, boolean includeGlobal) {
// Call super #getPermissions method, and just sort through those
Map<String, Boolean> perms = exportNodes(server, world, null, includeGlobal, true, null);
return perms.keySet().stream()
.filter(s -> Patterns.GROUP_MATCH.matcher(s).matches())
.map(s -> Patterns.DOT.split(s, 2)[1])
return getNodes().stream()
.filter(n -> n.shouldApplyOnWorld(world, includeGlobal, true))
.filter(n -> n.shouldApplyOnServer(server, includeGlobal, true))
.filter(Node::isGroupNode)
.map(Node::getGroupName)
.collect(Collectors.toList());
}
}
@@ -53,6 +53,6 @@ public class UpdateTask implements Runnable {
// Refresh all online users.
plugin.getUserManager().updateAllUsers();
plugin.getApiProvider().fireEvent(new PostSyncEvent());;
plugin.getApiProvider().fireEvent(new PostSyncEvent());
}
}
@@ -87,6 +87,7 @@ public abstract class Datastore {
public abstract boolean deleteTrack(Track track);
public abstract boolean saveUUIDData(String username, UUID uuid);
public abstract UUID getUUID(String username);
public abstract String getName(UUID uuid);
@@ -219,4 +220,11 @@ public abstract class Datastore {
doSync(() -> callback.onComplete(result));
});
}
public void getName(UUID uuid, Callback<String> callback) {
doAsync(() -> {
String result = getName(uuid);
doSync(() -> callback.onComplete(result));
});
}
}
@@ -0,0 +1,136 @@
package me.lucko.luckperms.storage;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.data.Log;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class SplitDatastore extends Datastore {
private final Map<String, Datastore> backing;
private final Map<String, String> types;
protected SplitDatastore(LuckPermsPlugin plugin, Map<String, Datastore> backing, Map<String, String> types) {
super(plugin, "Split Storage");
this.backing = ImmutableMap.copyOf(backing);
this.types = ImmutableMap.copyOf(types);
}
@Override
public void init() {
backing.values().forEach(Datastore::init);
for (Datastore ds : backing.values()) {
if (!ds.isAcceptingLogins()) {
return;
}
}
setAcceptingLogins(true);
}
@Override
public void shutdown() {
backing.values().forEach(Datastore::shutdown);
}
@Override
public boolean logAction(LogEntry entry) {
return backing.get(types.get("log")).logAction(entry);
}
@Override
public Log getLog() {
return backing.get(types.get("log")).getLog();
}
@Override
public boolean loadUser(UUID uuid, String username) {
return backing.get(types.get("user")).loadUser(uuid, username);
}
@Override
public boolean saveUser(User user) {
return backing.get(types.get("user")).saveUser(user);
}
@Override
public boolean cleanupUsers() {
return backing.get(types.get("user")).cleanupUsers();
}
@Override
public Set<UUID> getUniqueUsers() {
return backing.get(types.get("user")).getUniqueUsers();
}
@Override
public boolean createAndLoadGroup(String name) {
return backing.get(types.get("group")).createAndLoadGroup(name);
}
@Override
public boolean loadGroup(String name) {
return backing.get(types.get("group")).loadGroup(name);
}
@Override
public boolean loadAllGroups() {
return backing.get(types.get("group")).loadAllGroups();
}
@Override
public boolean saveGroup(Group group) {
return backing.get(types.get("group")).saveGroup(group);
}
@Override
public boolean deleteGroup(Group group) {
return backing.get(types.get("group")).deleteGroup(group);
}
@Override
public boolean createAndLoadTrack(String name) {
return backing.get(types.get("track")).createAndLoadTrack(name);
}
@Override
public boolean loadTrack(String name) {
return backing.get(types.get("track")).loadTrack(name);
}
@Override
public boolean loadAllTracks() {
return backing.get(types.get("track")).loadAllTracks();
}
@Override
public boolean saveTrack(Track track) {
return backing.get(types.get("track")).saveTrack(track);
}
@Override
public boolean deleteTrack(Track track) {
return backing.get(types.get("track")).deleteTrack(track);
}
@Override
public boolean saveUUIDData(String username, UUID uuid) {
return backing.get(types.get("uuid")).saveUUIDData(username, uuid);
}
@Override
public UUID getUUID(String username) {
return backing.get(types.get("uuid")).getUUID(username);
}
@Override
public String getName(UUID uuid) {
return backing.get(types.get("uuid")).getName(uuid);
}
}
@@ -28,48 +28,75 @@ import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.storage.methods.*;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@UtilityClass
public class StorageFactory {
private static final Set<String> TYPES = ImmutableSet.of("flatfile", "mongodb", "mysql", "sqlite", "h2");
private static final Set<String> TYPES = ImmutableSet.of("json", "yaml", "flatfile", "mongodb", "mysql", "sqlite", "h2");
@SuppressWarnings("unchecked")
public static Datastore getDatastore(LuckPermsPlugin plugin, String defaultMethod) {
Datastore datastore;
plugin.getLog().info("Detecting storage method...");
String storageMethod = plugin.getConfiguration().getStorageMethod().toLowerCase();
if (plugin.getConfiguration().getSplitStorage()) {
plugin.getLog().info("Using split storage.");
if (!TYPES.contains(storageMethod)) {
plugin.getLog().severe("Storage method '" + storageMethod + "' not recognised. Using the default instead.");
storageMethod = defaultMethod;
}
// java sucks
Map<String, String> types = (Map<String, String>) plugin.getConfiguration().getSplitStorageOptions();
switch (storageMethod) {
case "mysql":
plugin.getLog().info("Using MySQL as storage method.");
datastore = new MySQLDatastore(plugin, plugin.getConfiguration().getDatabaseValues());
break;
case "sqlite":
plugin.getLog().info("Using SQLite as storage method.");
datastore = new SQLiteDatastore(plugin, new File(plugin.getDataFolder(), "luckperms.sqlite"));
break;
case "h2":
plugin.getLog().info("Using H2 as storage method.");
datastore = new H2Datastore(plugin, new File(plugin.getDataFolder(), "luckperms.db"));
break;
case "mongodb":
plugin.getLog().info("Using MongoDB as storage method.");
datastore = new MongoDBDatastore(plugin, plugin.getConfiguration().getDatabaseValues());
break;
default:
plugin.getLog().info("Using Flatfile (JSON) as storage method.");
datastore = new FlatfileDatastore(plugin, plugin.getDataFolder());
break;
types.entrySet().stream()
.filter(e -> !TYPES.contains(e.getValue().toLowerCase()))
.forEach(e -> {
plugin.getLog().severe("Storage method for " + e.getKey() + " - " + e.getValue() + " not recognised. " +
"Using the default instead.");
e.setValue(defaultMethod);
});
Set<String> neededTypes = new HashSet<>();
neededTypes.addAll(types.values());
Map<String, Datastore> backing = new HashMap<>();
for (String type : neededTypes) {
backing.put(type, fromString(type, plugin));
}
datastore = new SplitDatastore(plugin, backing, types);
} else {
String storageMethod = plugin.getConfiguration().getStorageMethod().toLowerCase();
if (!TYPES.contains(storageMethod)) {
plugin.getLog().severe("Storage method '" + storageMethod + "' not recognised. Using the default instead.");
storageMethod = defaultMethod;
}
datastore = fromString(storageMethod, plugin);
plugin.getLog().info("Using " + datastore.getName() + " as storage method.");
}
plugin.getLog().info("Initialising datastore...");
datastore.init();
return datastore;
}
private static Datastore fromString(String storageMethod, LuckPermsPlugin plugin) {
switch (storageMethod) {
case "mysql":
return new MySQLDatastore(plugin, plugin.getConfiguration().getDatabaseValues());
case "sqlite":
return new SQLiteDatastore(plugin, new File(plugin.getDataFolder(), "luckperms.sqlite"));
case "h2":
return new H2Datastore(plugin, new File(plugin.getDataFolder(), "luckperms.db"));
case "mongodb":
return new MongoDBDatastore(plugin, plugin.getConfiguration().getDatabaseValues());
case "yaml":
return new YAMLDatastore(plugin, plugin.getDataFolder());
default:
return new JSONDatastore(plugin, plugin.getDataFolder());
}
}
}
@@ -22,75 +22,37 @@
package me.lucko.luckperms.storage.methods;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.data.Log;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.Node;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.*;
import java.util.logging.Formatter;
import java.util.stream.Collectors;
import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
@SuppressWarnings({"ResultOfMethodCallIgnored", "UnnecessaryLocalVariable"})
public class FlatfileDatastore extends Datastore {
abstract class FlatfileDatastore extends Datastore {
private static final String LOG_FORMAT = "%s(%s): [%s] %s(%s) --> %s";
private final Logger actionLogger = Logger.getLogger("lp_actions");
private Map<String, String> uuidCache = new ConcurrentHashMap<>();
private final File pluginDir;
private File usersDir;
private File groupsDir;
private File tracksDir;
private File uuidData;
private File actionLog;
final File pluginDir;
File usersDir;
File groupsDir;
File tracksDir;
File uuidData;
File actionLog;
public FlatfileDatastore(LuckPermsPlugin plugin, File pluginDir) {
super(plugin, "Flatfile - JSON");
FlatfileDatastore(LuckPermsPlugin plugin, String name, File pluginDir) {
super(plugin, name);
this.pluginDir = pluginDir;
}
private boolean doWrite(File file, WriteOperation writeOperation) {
boolean success = false;
try {
@Cleanup FileWriter fileWriter = new FileWriter(file);
@Cleanup BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
@Cleanup JsonWriter jsonWriter = new JsonWriter(bufferedWriter);
jsonWriter.setIndent(" ");
success = writeOperation.onRun(jsonWriter);
jsonWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
private boolean doRead(File file, ReadOperation readOperation) {
boolean success = false;
try {
@Cleanup FileReader fileReader = new FileReader(file);
@Cleanup BufferedReader bufferedReader = new BufferedReader(fileReader);
@Cleanup JsonReader jsonReader = new JsonReader(bufferedReader);
success = readOperation.onRun(jsonReader);
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
@Override
public void init() {
try {
@@ -119,7 +81,6 @@ public class FlatfileDatastore extends Datastore {
}
cleanupUsers();
setAcceptingLogins(true);
}
@@ -167,404 +128,6 @@ public class FlatfileDatastore extends Datastore {
return Log.builder().build();
}
@Override
public boolean loadUser(UUID uuid, String username) {
User user = plugin.getUserManager().make(uuid, username);
boolean success = false;
File userFile = new File(usersDir, uuid.toString() + ".json");
if (userFile.exists()) {
final String[] name = new String[1];
success = doRead(userFile, reader -> {
reader.beginObject();
reader.nextName(); // uuid record
reader.nextString(); // uuid
reader.nextName(); // name record
name[0] = reader.nextString(); // name
reader.nextName(); // primaryGroup record
user.setPrimaryGroup(reader.nextString()); // primaryGroup
reader.nextName(); //perms
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
user.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
reader.endObject();
return true;
});
if (user.getName().equalsIgnoreCase("null")) {
user.setName(name[0]);
} else {
if (!name[0].equals(user.getName())) {
doWrite(userFile, writer -> {
writer.beginObject();
writer.name("uuid").value(user.getUuid().toString());
writer.name("name").value(user.getName());
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
}
}
} else {
success = true;
}
if (success) plugin.getUserManager().updateOrSet(user);
return success;
}
@Override
public boolean saveUser(User user) {
File userFile = new File(usersDir, user.getUuid().toString() + ".json");
if (!userFile.exists()) {
try {
userFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean success = doWrite(userFile, writer -> {
writer.beginObject();
writer.name("uuid").value(user.getUuid().toString());
writer.name("name").value(user.getName());
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
return success;
}
@Override
public boolean cleanupUsers() {
File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".json"));
if (files == null) return false;
for (File file : files) {
Map<String, Boolean> nodes = new HashMap<>();
doRead(file, reader -> {
reader.beginObject();
reader.nextName(); // uuid record
reader.nextString(); // uuid
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); // primaryGroup record
reader.nextString(); // primaryGroup
reader.nextName(); //perms
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
nodes.put(node, b);
}
reader.endObject();
reader.endObject();
return true;
});
boolean shouldDelete = false;
if (nodes.size() == 1) {
for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
// There's only one
shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue();
}
}
if (shouldDelete) {
file.delete();
}
}
return true;
}
@Override
public Set<UUID> getUniqueUsers() {
String[] fileNames = usersDir.list((dir, name) -> name.endsWith(".json"));
if (fileNames == null) return null;
return Arrays.stream(fileNames)
.map(s -> s.substring(0, s.length() - 5))
.map(UUID::fromString)
.collect(Collectors.toSet());
}
@Override
public boolean createAndLoadGroup(String name) {
Group group = plugin.getGroupManager().make(name);
File groupFile = new File(groupsDir, name + ".json");
if (!groupFile.exists()) {
try {
groupFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
boolean success = doWrite(groupFile, writer -> {
writer.beginObject();
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
if (!success) return false;
}
boolean success = doRead(groupFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); //perms
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
group.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
reader.endObject();
return true;
});
if (success) plugin.getGroupManager().updateOrSet(group);
return success;
}
@Override
public boolean loadGroup(String name) {
Group group = plugin.getGroupManager().make(name);
File groupFile = new File(groupsDir, name + ".json");
if (!groupFile.exists()) {
return false;
}
boolean success = doRead(groupFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); //perms
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
group.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
reader.endObject();
return true;
});
if (success) plugin.getGroupManager().updateOrSet(group);
return success;
}
@Override
public boolean loadAllGroups() {
String[] fileNames = groupsDir.list((dir, name) -> name.endsWith(".json"));
if (fileNames == null) return false;
List<String> groups = Arrays.stream(fileNames)
.map(s -> s.substring(0, s.length() - 5))
.collect(Collectors.toList());
plugin.getGroupManager().unloadAll();
groups.forEach(this::loadGroup);
return true;
}
@Override
public boolean saveGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".json");
if (!groupFile.exists()) {
try {
groupFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean success = doWrite(groupFile, writer -> {
writer.beginObject();
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
return success;
}
@Override
public boolean deleteGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".json");
if (groupFile.exists()) {
groupFile.delete();
}
return true;
}
@Override
public boolean createAndLoadTrack(String name) {
Track track = plugin.getTrackManager().make(name);
List<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".json");
if (!trackFile.exists()) {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
boolean success = doWrite(trackFile, writer -> {
writer.beginObject();
writer.name("name").value(track.getName());
writer.name("groups");
writer.beginArray();
for (String s : track.getGroups()) {
writer.value(s);
}
writer.endArray();
writer.endObject();
return true;
});
if (!success) return false;
}
boolean success = doRead(trackFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); // groups record
reader.beginArray();
while (reader.hasNext()) {
groups.add(reader.nextString());
}
reader.endArray();
reader.endObject();
return true;
});
track.setGroups(groups);
if (success) plugin.getTrackManager().updateOrSet(track);
return success;
}
@Override
public boolean loadTrack(String name) {
Track track = plugin.getTrackManager().make(name);
List<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".json");
if (!trackFile.exists()) {
return false;
}
boolean success = doRead(trackFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); // groups record
reader.beginArray();
while (reader.hasNext()) {
groups.add(reader.nextString());
}
reader.endArray();
reader.endObject();
return true;
});
track.setGroups(groups);
if (success) plugin.getTrackManager().updateOrSet(track);
return success;
}
@Override
public boolean loadAllTracks() {
String[] fileNames = tracksDir.list((dir, name) -> name.endsWith(".json"));
if (fileNames == null) return false;
List<String> tracks = Arrays.stream(fileNames)
.map(s -> s.substring(0, s.length() - 5))
.collect(Collectors.toList());
plugin.getTrackManager().unloadAll();
tracks.forEach(this::loadTrack);
return true;
}
@Override
public boolean saveTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".json");
if (!trackFile.exists()) {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean success = doWrite(trackFile, writer -> {
writer.beginObject();
writer.name("name").value(track.getName());
writer.name("groups");
writer.beginArray();
for (String s : track.getGroups()) {
writer.value(s);
}
writer.endArray();
writer.endObject();
return true;
});
return success;
}
@Override
public boolean deleteTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".json");
if (trackFile.exists()) {
trackFile.delete();
}
return true;
}
private Map<String, String> getUUIDCache() {
Map<String, String> cache = new HashMap<>();
@@ -612,11 +175,13 @@ public class FlatfileDatastore extends Datastore {
return UUID.fromString(uuidCache.get(username));
}
interface WriteOperation {
boolean onRun(JsonWriter writer) throws IOException;
}
interface ReadOperation {
boolean onRun(JsonReader reader) throws IOException;
@Override
public String getName(UUID uuid) {
for (Map.Entry<String, String> e : uuidCache.entrySet()) {
if (e.getValue().equalsIgnoreCase(uuid.toString())) {
return e.getKey();
}
}
return null;
}
}
@@ -66,6 +66,7 @@ public class H2Datastore extends SQLDatastore {
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryPS.getQuery());
queryPS.onRun(preparedStatement);
preparedStatement.execute();
success = true;
} catch (SQLException e) {
e.printStackTrace();
@@ -84,7 +85,6 @@ public class H2Datastore extends SQLDatastore {
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryRS.getQuery());
queryRS.onRun(preparedStatement);
preparedStatement.execute();
@Cleanup ResultSet resultSet = preparedStatement.executeQuery();
success = queryRS.onResult(resultSet);
@@ -0,0 +1,486 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.storage.methods;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.Node;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
@SuppressWarnings({"ResultOfMethodCallIgnored", "UnnecessaryLocalVariable"})
public class JSONDatastore extends FlatfileDatastore {
public JSONDatastore(LuckPermsPlugin plugin, File pluginDir) {
super(plugin, "Flatfile - JSON", pluginDir);
}
private boolean doWrite(File file, WriteOperation writeOperation) {
boolean success = false;
try {
@Cleanup FileWriter fileWriter = new FileWriter(file);
@Cleanup BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
@Cleanup JsonWriter jsonWriter = new JsonWriter(bufferedWriter);
jsonWriter.setIndent(" ");
success = writeOperation.onRun(jsonWriter);
jsonWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
private boolean doRead(File file, ReadOperation readOperation) {
boolean success = false;
try {
@Cleanup FileReader fileReader = new FileReader(file);
@Cleanup BufferedReader bufferedReader = new BufferedReader(fileReader);
@Cleanup JsonReader jsonReader = new JsonReader(bufferedReader);
success = readOperation.onRun(jsonReader);
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
@Override
public boolean loadUser(UUID uuid, String username) {
User user = plugin.getUserManager().make(uuid, username);
boolean success = false;
File userFile = new File(usersDir, uuid.toString() + ".json");
if (userFile.exists()) {
final String[] name = new String[1];
success = doRead(userFile, reader -> {
reader.beginObject();
reader.nextName(); // uuid record
reader.nextString(); // uuid
reader.nextName(); // name record
name[0] = reader.nextString(); // name
reader.nextName(); // primaryGroup record
user.setPrimaryGroup(reader.nextString()); // primaryGroup
reader.nextName(); //perms
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
user.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
reader.endObject();
return true;
});
if (user.getName().equalsIgnoreCase("null")) {
user.setName(name[0]);
} else {
if (!name[0].equals(user.getName())) {
doWrite(userFile, writer -> {
writer.beginObject();
writer.name("uuid").value(user.getUuid().toString());
writer.name("name").value(user.getName());
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
}
}
} else {
success = true;
}
if (success) plugin.getUserManager().updateOrSet(user);
return success;
}
@Override
public boolean saveUser(User user) {
File userFile = new File(usersDir, user.getUuid().toString() + ".json");
if (!plugin.getUserManager().shouldSave(user)) {
if (userFile.exists()) {
userFile.delete();
}
return true;
}
if (!userFile.exists()) {
try {
userFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean success = doWrite(userFile, writer -> {
writer.beginObject();
writer.name("uuid").value(user.getUuid().toString());
writer.name("name").value(user.getName());
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
return success;
}
@Override
public boolean cleanupUsers() {
File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".json"));
if (files == null) return false;
for (File file : files) {
Map<String, Boolean> nodes = new HashMap<>();
doRead(file, reader -> {
reader.beginObject();
reader.nextName(); // uuid record
reader.nextString(); // uuid
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); // primaryGroup record
reader.nextString(); // primaryGroup
reader.nextName(); //perms
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
nodes.put(node, b);
}
reader.endObject();
reader.endObject();
return true;
});
boolean shouldDelete = false;
if (nodes.size() == 1) {
for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
// There's only one
shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue();
}
}
if (shouldDelete) {
file.delete();
}
}
return true;
}
@Override
public Set<UUID> getUniqueUsers() {
String[] fileNames = usersDir.list((dir, name) -> name.endsWith(".json"));
if (fileNames == null) return null;
return Arrays.stream(fileNames)
.map(s -> s.substring(0, s.length() - 5))
.map(UUID::fromString)
.collect(Collectors.toSet());
}
@Override
public boolean createAndLoadGroup(String name) {
Group group = plugin.getGroupManager().make(name);
File groupFile = new File(groupsDir, name + ".json");
if (!groupFile.exists()) {
try {
groupFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
boolean success = doWrite(groupFile, writer -> {
writer.beginObject();
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
if (!success) return false;
}
boolean success = doRead(groupFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); //perms
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
group.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
reader.endObject();
return true;
});
if (success) plugin.getGroupManager().updateOrSet(group);
return success;
}
@Override
public boolean loadGroup(String name) {
Group group = plugin.getGroupManager().make(name);
File groupFile = new File(groupsDir, name + ".json");
if (!groupFile.exists()) {
return false;
}
boolean success = doRead(groupFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); //perms
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
group.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
reader.endObject();
return true;
});
if (success) plugin.getGroupManager().updateOrSet(group);
return success;
}
@Override
public boolean loadAllGroups() {
String[] fileNames = groupsDir.list((dir, name) -> name.endsWith(".json"));
if (fileNames == null) return false;
List<String> groups = Arrays.stream(fileNames)
.map(s -> s.substring(0, s.length() - 5))
.collect(Collectors.toList());
plugin.getGroupManager().unloadAll();
groups.forEach(this::loadGroup);
return true;
}
@Override
public boolean saveGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".json");
if (!groupFile.exists()) {
try {
groupFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean success = doWrite(groupFile, writer -> {
writer.beginObject();
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
return success;
}
@Override
public boolean deleteGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".json");
if (groupFile.exists()) {
groupFile.delete();
}
return true;
}
@Override
public boolean createAndLoadTrack(String name) {
Track track = plugin.getTrackManager().make(name);
List<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".json");
if (!trackFile.exists()) {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
boolean success = doWrite(trackFile, writer -> {
writer.beginObject();
writer.name("name").value(track.getName());
writer.name("groups");
writer.beginArray();
for (String s : track.getGroups()) {
writer.value(s);
}
writer.endArray();
writer.endObject();
return true;
});
if (!success) return false;
}
boolean success = doRead(trackFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); // groups record
reader.beginArray();
while (reader.hasNext()) {
groups.add(reader.nextString());
}
reader.endArray();
reader.endObject();
return true;
});
track.setGroups(groups);
if (success) plugin.getTrackManager().updateOrSet(track);
return success;
}
@Override
public boolean loadTrack(String name) {
Track track = plugin.getTrackManager().make(name);
List<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".json");
if (!trackFile.exists()) {
return false;
}
boolean success = doRead(trackFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); // groups record
reader.beginArray();
while (reader.hasNext()) {
groups.add(reader.nextString());
}
reader.endArray();
reader.endObject();
return true;
});
track.setGroups(groups);
if (success) plugin.getTrackManager().updateOrSet(track);
return success;
}
@Override
public boolean loadAllTracks() {
String[] fileNames = tracksDir.list((dir, name) -> name.endsWith(".json"));
if (fileNames == null) return false;
List<String> tracks = Arrays.stream(fileNames)
.map(s -> s.substring(0, s.length() - 5))
.collect(Collectors.toList());
plugin.getTrackManager().unloadAll();
tracks.forEach(this::loadTrack);
return true;
}
@Override
public boolean saveTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".json");
if (!trackFile.exists()) {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean success = doWrite(trackFile, writer -> {
writer.beginObject();
writer.name("name").value(track.getName());
writer.name("groups");
writer.beginArray();
for (String s : track.getGroups()) {
writer.value(s);
}
writer.endArray();
writer.endObject();
return true;
});
return success;
}
@Override
public boolean deleteTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".json");
if (trackFile.exists()) {
trackFile.delete();
}
return true;
}
interface WriteOperation {
boolean onRun(JsonWriter writer) throws IOException;
}
interface ReadOperation {
boolean onRun(JsonReader reader) throws IOException;
}
}
@@ -169,7 +169,11 @@ public class MongoDBDatastore extends Datastore {
@Override
public boolean saveUser(User user) {
if (!plugin.getUserManager().shouldSave(user)) {
return true;
boolean success = call(() -> {
MongoCollection<Document> c = database.getCollection("users");
return c.deleteOne(new Document("_id", user.getUuid())).wasAcknowledged();
}, false);
return success;
}
boolean success = call(() -> {
@@ -413,6 +417,20 @@ public class MongoDBDatastore extends Datastore {
}, null);
}
@Override
public String getName(UUID uuid) {
return call(() -> {
MongoCollection<Document> c = database.getCollection("uuid");
try (MongoCursor<Document> cursor = c.find(new Document("_id", uuid)).iterator()) {
if (cursor.hasNext()) {
return cursor.next().get("name", String.class);
}
}
return null;
}, null);
}
private static <T> T call(Callable<T> c, T def) {
try {
return c.call();
@@ -427,14 +445,12 @@ public class MongoDBDatastore extends Datastore {
The following two methods convert the node maps so they can be stored. */
private static <V> Map<String, V> convert(Map<String, V> map) {
return map.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey().replace(".", "[**DOT**]").replace("$", "[**DOLLAR**]"), e.getValue()))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
.collect(Collectors.toMap(e -> e.getKey().replace(".", "[**DOT**]").replace("$", "[**DOLLAR**]"), Map.Entry::getValue));
}
private static <V> Map<String, V> revert(Map<String, V> map) {
return map.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey().replace("[**DOT**]", ".").replace("[**DOLLAR**]", "$"), e.getValue()))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
.collect(Collectors.toMap(e -> e.getKey().replace("[**DOT**]", ".").replace("[**DOLLAR**]", "$"), Map.Entry::getValue));
}
private static Document fromUser(User user) {
@@ -85,6 +85,7 @@ public class MySQLDatastore extends SQLDatastore {
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryPS.getQuery());
queryPS.onRun(preparedStatement);
preparedStatement.execute();
success = true;
} catch (SQLException e) {
e.printStackTrace();
@@ -103,7 +104,6 @@ public class MySQLDatastore extends SQLDatastore {
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryRS.getQuery());
queryRS.onRun(preparedStatement);
preparedStatement.execute();
@Cleanup ResultSet resultSet = preparedStatement.executeQuery();
success = queryRS.onResult(resultSet);
@@ -55,7 +55,8 @@ abstract class SQLDatastore extends Datastore {
private static final String USER_SELECT = "SELECT * FROM lp_users WHERE uuid=?";
private static final String USER_SELECT_ALL = "SELECT uuid FROM lp_users";
private static final String USER_UPDATE = "UPDATE lp_users SET name=?, primary_group = ?, perms=? WHERE uuid=?";
private static final String USER_DELETE = "DELETE FROM lp_users WHERE perms=?";
private static final String USER_DELETE = "DELETE FROM lp_users WHERE uuid=?";
private static final String USER_DELETE_ALL = "DELETE FROM lp_users WHERE perms=?";
private static final String GROUP_INSERT = "INSERT INTO lp_groups VALUES(?, ?)";
private static final String GROUP_SELECT = "SELECT perms FROM lp_groups WHERE name=?";
@@ -71,6 +72,7 @@ abstract class SQLDatastore extends Datastore {
private static final String UUIDCACHE_INSERT = "INSERT INTO lp_uuid VALUES(?, ?)";
private static final String UUIDCACHE_SELECT = "SELECT uuid FROM lp_uuid WHERE name=?";
private static final String UUIDCACHE_SELECT_NAME = "SELECT name FROM lp_uuid WHERE uuid=?";
private static final String UUIDCACHE_UPDATE = "UPDATE lp_uuid SET uuid=? WHERE name=?";
private static final String ACTION_INSERT = "INSERT INTO lp_actions(`time`, `actor_uuid`, `actor_name`, `type`, `acted_uuid`, `acted_name`, `action`) VALUES(?, ?, ?, ?, ?, ?, ?)";
@@ -188,7 +190,13 @@ abstract class SQLDatastore extends Datastore {
@Override
public boolean saveUser(User user) {
if (!plugin.getUserManager().shouldSave(user)) {
return true;
boolean success = runQuery(new QueryPS(USER_DELETE) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, user.getUuid().toString());
}
});
return success;
}
boolean success = runQuery(new QueryRS(USER_SELECT) {
@@ -233,7 +241,7 @@ abstract class SQLDatastore extends Datastore {
@Override
public boolean cleanupUsers() {
boolean success = runQuery(new QueryPS(USER_DELETE) {
boolean success = runQuery(new QueryPS(USER_DELETE_ALL) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, "{\"group.default\":true}");
@@ -541,6 +549,30 @@ abstract class SQLDatastore extends Datastore {
return success ? uuid[0] : null;
}
@Override
public String getName(UUID uuid) {
final String u = uuid.toString();
final String[] name = {null};
boolean success = runQuery(new QueryRS(UUIDCACHE_SELECT_NAME) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, u);
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
if (resultSet.next()) {
name[0] = resultSet.getString("name");
return true;
}
return false;
}
});
return success ? name[0] : null;
}
private class Query extends QueryPS {
Query(String query) {
super(query);
@@ -66,6 +66,7 @@ public class SQLiteDatastore extends SQLDatastore {
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryPS.getQuery());
queryPS.onRun(preparedStatement);
preparedStatement.execute();
success = true;
} catch (SQLException e) {
e.printStackTrace();
@@ -84,7 +85,6 @@ public class SQLiteDatastore extends SQLDatastore {
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryRS.getQuery());
queryRS.onRun(preparedStatement);
preparedStatement.execute();
@Cleanup ResultSet resultSet = preparedStatement.executeQuery();
success = queryRS.onResult(resultSet);
@@ -0,0 +1,372 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.storage.methods;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.Node;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
@SuppressWarnings({"unchecked", "ResultOfMethodCallIgnored"})
public class YAMLDatastore extends FlatfileDatastore {
public YAMLDatastore(LuckPermsPlugin plugin, File pluginDir) {
super(plugin, "Flatfile - YAML", pluginDir);
}
private static Yaml getYaml() {
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
return new Yaml(options);
}
private boolean doRead(File file, ReadOperation readOperation) {
boolean success = false;
try {
@Cleanup FileReader fileReader = new FileReader(file);
@Cleanup BufferedReader bufferedReader = new BufferedReader(fileReader);
success = readOperation.onRun((Map<String, Object>) getYaml().load(bufferedReader));
} catch (Throwable t) {
t.printStackTrace();
}
return success;
}
private boolean doWrite(File file, Map<String, Object> values) {
try {
@Cleanup FileWriter fileWriter = new FileWriter(file);
@Cleanup BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
getYaml().dump(values, bufferedWriter);
bufferedWriter.flush();
return true;
} catch (Throwable t) {
t.printStackTrace();
return false;
}
}
@Override
public boolean loadUser(UUID uuid, String username) {
User user = plugin.getUserManager().make(uuid, username);
boolean success = false;
File userFile = new File(usersDir, uuid.toString() + ".yml");
if (userFile.exists()) {
final String[] name = {null};
success = doRead(userFile, values -> {
name[0] = (String) values.get("name");
user.setPrimaryGroup((String) values.get("primary-group"));
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
for (Map.Entry<String, Boolean> e : perms.entrySet()) {
user.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue()));
}
return true;
});
if (user.getName().equalsIgnoreCase("null")) {
user.setName(name[0]);
} else {
if (!name[0].equals(user.getName())) {
Map<String, Object> values = new HashMap<>();
values.put("uuid", user.getUuid().toString());
values.put("name", user.getName());
values.put("primary-group", user.getPrimaryGroup());
values.put("perms", exportToLegacy(user.getNodes()));
doWrite(userFile, values);
}
}
} else {
success = true;
}
if (success) plugin.getUserManager().updateOrSet(user);
return success;
}
@Override
public boolean saveUser(User user) {
File userFile = new File(usersDir, user.getUuid().toString() + ".yml");
if (!plugin.getUserManager().shouldSave(user)) {
if (userFile.exists()) {
userFile.delete();
}
return true;
}
if (!userFile.exists()) {
try {
userFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
Map<String, Object> values = new HashMap<>();
values.put("uuid", user.getUuid().toString());
values.put("name", user.getName());
values.put("primary-group", user.getPrimaryGroup());
values.put("perms", exportToLegacy(user.getNodes()));
return doWrite(userFile, values);
}
@Override
public boolean cleanupUsers() {
File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".yml"));
if (files == null) return false;
for (File file : files) {
Map<String, Boolean> nodes = new HashMap<>();
doRead(file, values -> {
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
nodes.putAll(perms);
return true;
});
boolean shouldDelete = false;
if (nodes.size() == 1) {
for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
// There's only one
shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue();
}
}
if (shouldDelete) {
file.delete();
}
}
return true;
}
@Override
public Set<UUID> getUniqueUsers() {
String[] fileNames = usersDir.list((dir, name) -> name.endsWith(".yml"));
if (fileNames == null) return null;
return Arrays.stream(fileNames)
.map(s -> s.substring(0, s.length() - 4))
.map(UUID::fromString)
.collect(Collectors.toSet());
}
@Override
public boolean createAndLoadGroup(String name) {
Group group = plugin.getGroupManager().make(name);
File groupFile = new File(groupsDir, name + ".yml");
if (!groupFile.exists()) {
try {
groupFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
Map<String, Object> values = new HashMap<>();
values.put("name", group.getName());
values.put("perms", exportToLegacy(group.getNodes()));
if (!doWrite(groupFile, values)) {
return false;
}
}
boolean success = doRead(groupFile, values -> {
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
for (Map.Entry<String, Boolean> e : perms.entrySet()) {
group.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue()));
}
return true;
});
if (success) plugin.getGroupManager().updateOrSet(group);
return success;
}
@Override
public boolean loadGroup(String name) {
Group group = plugin.getGroupManager().make(name);
File groupFile = new File(groupsDir, name + ".yml");
if (!groupFile.exists()) {
return false;
}
boolean success = doRead(groupFile, values -> {
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
for (Map.Entry<String, Boolean> e : perms.entrySet()) {
group.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue()));
}
return true;
});
if (success) plugin.getGroupManager().updateOrSet(group);
return success;
}
@Override
public boolean loadAllGroups() {
String[] fileNames = groupsDir.list((dir, name) -> name.endsWith(".yml"));
if (fileNames == null) return false;
List<String> groups = Arrays.stream(fileNames)
.map(s -> s.substring(0, s.length() - 4))
.collect(Collectors.toList());
plugin.getGroupManager().unloadAll();
groups.forEach(this::loadGroup);
return true;
}
@Override
public boolean saveGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".yml");
if (!groupFile.exists()) {
try {
groupFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
Map<String, Object> values = new HashMap<>();
values.put("name", group.getName());
values.put("perms", exportToLegacy(group.getNodes()));
return doWrite(groupFile, values);
}
@Override
public boolean deleteGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".yml");
if (groupFile.exists()) {
groupFile.delete();
}
return true;
}
@Override
public boolean createAndLoadTrack(String name) {
Track track = plugin.getTrackManager().make(name);
List<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".yml");
if (!trackFile.exists()) {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
Map<String, Object> values = new HashMap<>();
values.put("name", track.getName());
values.put("groups", track.getGroups());
if (!doWrite(trackFile, values)) {
return false;
}
}
boolean success = doRead(trackFile, values -> {
groups.addAll((List<String>) values.get("groups"));
return true;
});
track.setGroups(groups);
if (success) plugin.getTrackManager().updateOrSet(track);
return success;
}
@Override
public boolean loadTrack(String name) {
Track track = plugin.getTrackManager().make(name);
List<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".yml");
if (!trackFile.exists()) {
return false;
}
boolean success = doRead(trackFile, values -> {
groups.addAll((List<String>) values.get("groups"));
return true;
});
track.setGroups(groups);
if (success) plugin.getTrackManager().updateOrSet(track);
return success;
}
@Override
public boolean loadAllTracks() {
String[] fileNames = tracksDir.list((dir, name) -> name.endsWith(".yml"));
if (fileNames == null) return false;
List<String> tracks = Arrays.stream(fileNames)
.map(s -> s.substring(0, s.length() - 4))
.collect(Collectors.toList());
plugin.getTrackManager().unloadAll();
tracks.forEach(this::loadTrack);
return true;
}
@Override
public boolean saveTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".yml");
if (!trackFile.exists()) {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
Map<String, Object> values = new HashMap<>();
values.put("name", track.getName());
values.put("groups", track.getGroups());
return doWrite(trackFile, values);
}
@Override
public boolean deleteTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".yml");
if (trackFile.exists()) {
trackFile.delete();
}
return true;
}
interface ReadOperation {
boolean onRun(Map<String, Object> values);
}
}
@@ -35,7 +35,7 @@ public class TrackManager extends AbstractManager<String, Track> {
* @return a set of tracks that the groups could be a member of
*/
public Set<Track> getApplicableTracks(String group) {
return objects.values().stream()
return getAll().values().stream()
.filter(t -> t.containsGroup(group))
.collect(Collectors.toSet());
}
@@ -27,10 +27,10 @@ import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.event.events.GroupAddEvent;
import me.lucko.luckperms.api.implementation.internal.GroupLink;
import me.lucko.luckperms.api.implementation.internal.PermissionHolderLink;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -38,7 +38,6 @@ import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.Identifiable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -325,12 +324,13 @@ public abstract class User extends PermissionHolder implements Identifiable<UUID
* @param includeGlobal Whether to include global groups
* @return a {@link List} of group names
*/
private List<String> getGroups(String server, String world, boolean includeGlobal) {
public List<String> getGroups(String server, String world, boolean includeGlobal) {
// Call super #getPermissions method, and just sort through those
Map<String, Boolean> perms = exportNodes(server, world, null, includeGlobal, true, null);
return perms.keySet().stream()
.filter(s -> Patterns.GROUP_MATCH.matcher(s).matches())
.map(s -> Patterns.DOT.split(s, 2)[1])
return getNodes().stream()
.filter(n -> n.shouldApplyOnWorld(world, includeGlobal, true))
.filter(n -> n.shouldApplyOnServer(server, includeGlobal, true))
.filter(Node::isGroupNode)
.map(Node::getGroupName)
.collect(Collectors.toList());
}
}
@@ -45,7 +45,7 @@ public abstract class UserManager extends AbstractManager<UUID, User> {
@SuppressWarnings("OptionalGetWithoutIsPresent")
public User get(String name) {
try {
return objects.values().stream()
return getAll().values().stream()
.filter(u -> u.getName().equalsIgnoreCase(name))
.limit(1).findAny().get();
} catch (NoSuchElementException e) {
@@ -54,15 +54,17 @@ public abstract class UserManager extends AbstractManager<UUID, User> {
}
@Override
public void set(User u) {
public void preSet(User u) {
giveDefaultIfNeeded(u, true);
super.set(u);
}
@Override
public void copy(User from, User to) {
to.setNodes(from.getNodes());
to.setPrimaryGroup(from.getPrimaryGroup());
if (from.getPrimaryGroup() != null) {
// This isn't just a black user. we shouldn't override in that case.
to.setNodes(from.getNodes());
to.setPrimaryGroup(from.getPrimaryGroup());
}
to.refreshPermissions();
}
@@ -72,7 +74,7 @@ public abstract class UserManager extends AbstractManager<UUID, User> {
*/
public void giveDefaultIfNeeded(User user, boolean save) {
boolean hasGroup = false;
for (Node node : user.getPermissions()) {
for (Node node : user.getPermissions(false)) {
if (node.isGroupNode()) {
hasGroup = true;
break;
@@ -22,8 +22,10 @@
package me.lucko.luckperms.utils;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* An abstract manager class
@@ -31,10 +33,12 @@ import java.util.concurrent.ConcurrentHashMap;
* @param <T> the class this manager is "managing"
*/
public abstract class AbstractManager<I, T extends Identifiable<I>> {
protected final Map<I, T> objects = new ConcurrentHashMap<>();
private final Map<I, T> objects = new HashMap<>();
public Map<I, T> getAll() {
return objects;
public final Map<I, T> getAll() {
synchronized (objects) {
return ImmutableMap.copyOf(objects);
}
}
/**
@@ -42,28 +46,39 @@ public abstract class AbstractManager<I, T extends Identifiable<I>> {
* @param id The id to search by
* @return a {@link T} object if the object is loaded, returns null if the object is not loaded
*/
public T get(I id) {
return objects.get(id);
public final T get(I id) {
synchronized (objects) {
return objects.get(id);
}
}
/**
* Add a object to the loaded objects map
* @param t The object to add
*/
public void set(T t) {
objects.put(t.getId(), t);
public final void set(T t) {
preSet(t);
synchronized (objects) {
objects.put(t.getId(), t);
}
}
protected void preSet(T t) {
}
/**
* Updates (or sets if the object wasn't already loaded) an object in the objects map
* @param t The object to update or set
*/
public void updateOrSet(T t) {
if (!isLoaded(t.getId())) {
// The object isn't already loaded
set(t);
} else {
copy(t, objects.get(t.getId()));
public final void updateOrSet(T t) {
synchronized (objects) {
if (!isLoaded(t.getId())) {
// The object isn't already loaded
set(t);
} else {
copy(t, objects.get(t.getId()));
}
}
}
@@ -74,25 +89,36 @@ public abstract class AbstractManager<I, T extends Identifiable<I>> {
* @param id The id of the object
* @return true if the object is loaded
*/
public boolean isLoaded(I id) {
return objects.containsKey(id);
public final boolean isLoaded(I id) {
synchronized (objects) {
return objects.containsKey(id);
}
}
/**
* Removes and unloads the object from the manager
* @param t The object to unload
*/
public void unload(T t) {
public final void unload(T t) {
if (t != null) {
objects.remove(t.getId());
preUnload(t);
synchronized (objects) {
objects.remove(t.getId());
}
}
}
protected void preUnload(T t) {
}
/**
* Unloads all objects from the manager
*/
public void unloadAll() {
objects.clear();
public final void unloadAll() {
synchronized (objects) {
objects.clear();
}
}
/**
Loaded 100 of 110 files, more files were not shown because too many files have changed in this diff. Show more