Compare commits

...
89 Commits
Author SHA1 Message Date
Luck fa9324bda8 Implement basic standalone base, working towards #9 2016-09-20 21:50:28 +01:00
Luck 1e72f9d8fe Listen on lowest priority, may fix issues with plugins checking for permissions on login events 2016-09-20 21:04:53 +01:00
Luck 50fd954599 Use a BiMap in UuidCache 2016-09-19 21:13:45 +01:00
Luck b1e34b55f8 Re-add regex permissions & actually take note of the config options 2016-09-19 21:00:23 +01:00
Luck cff1b8a411 Separate PermissionProcessor into own class 2016-09-19 20:45:37 +01:00
Luck 6ffa6720be Make more specific wildcards take priority over less specific ones 2016-09-19 20:36:52 +01:00
Luck f52a106165 Order nodes alphabetically after other priority rules have been applied 2016-09-19 17:31:00 +01:00
Luck bf247b71e1 Fix possible race condition & maybe use better Hikari flags 2016-09-18 23:13:20 +01:00
Luck 66818e4c82 Fix build 2016-09-17 23:46:11 +01:00
Luck 23d53d39b4 Refactor permission calculation 2016-09-17 23:15:07 +01:00
Luck 30ea5a5b69 Fix no permissions applying when include-global is false, and better support per-server/per-world groups 2016-09-17 19:05:56 +01:00
Luck e7a210dc06 Update default configs 2016-09-16 22:09:55 +01:00
Luck 497de9824a Actually pass on a single wildcard node so it gets applied 2016-09-16 21:46:55 +01:00
Luck be74b50762 Fixes for offline mode servers 2016-09-16 21:24:25 +01:00
Luck f2e06b56e7 Cache permission lookups 2016-09-16 21:11:12 +01:00
Luck f6aa20c300 Fix compile 2016-09-15 17:12:19 +01:00
Luck 5e16134f6f Provide prefixes and suffixes through the PS options 2016-09-15 17:06:27 +01:00
Luck 6165fcbb21 Fix #getDefaults for users 2016-09-15 16:54:12 +01:00
Luck 831e882db0 Actually apply default permissions on Sponge 2016-09-15 16:53:03 +01:00
Luck feb8bf4bd3 Update readme 2016-09-14 20:16:36 +01:00
Luck ce3ee866ca Sponge fixes 2016-09-14 19:37:20 +01:00
Luck d29dd35689 Support the latest powerfulperms so-called "api" 2016-09-13 20:14:16 +01:00
Luck 3a0a89853f Apply negated wildcards 2016-09-13 17:23:16 +01:00
Luck e7ca79d251 Use Splitter instead of Patterns, don't split arguments in quotes 2016-09-12 19:00:37 +01:00
Luck 22426bab33 Update placeholders to use the latest API version 2016-09-11 15:36:19 +01:00
Luck 116094ea96 Maybe fix an issue with per-server prefixes/suffixes 2016-09-11 14:34:38 +01:00
Luck 9df39a9545 Inject PermissibleBase instead of using attachments. Resolves #14 2016-09-11 11:50:59 +01:00
Luck b8f44df723 Add missing headers 2016-09-10 22:40:36 +01:00
Luck 74f275eb69 Add more info the importer error output 2016-09-10 22:08:07 +01:00
Luck 1142cbcb2c Add platform type to the info command 2016-09-10 21:54:31 +01:00
Luck 37749e5829 Don't apply the default group when exporting users 2016-09-10 16:53:04 +01:00
Luck f9025c7a51 fix #getUniqueUsers for SQL storage types 2016-09-10 16:38:37 +01:00
Luck c7bfc38477 Build an extra jar with guava and gson shaded to support 1.7 2016-09-10 16:32:37 +01:00
Luck 7ca394d7b9 Add some extra console output to the debug command 2016-09-10 14:35:12 +01:00
Luck 31884ea59d Maybe fix race condition with messages not sending 2016-09-10 14:31:42 +01:00
Luck e79b7c7023 Update readme 2016-09-10 09:04:59 +01:00
Luck 1fdb4e8ade Merge pull request #11 from HeroCC/patch-1
Fix PermissionsEx false node migration
2016-09-09 21:49:43 +01:00
HeroCC 53db8c5928 Fix PermissionsEx false node migration
PermissionsEx false nodes begin with a '-' not a '!'
2016-09-09 16:39:13 -04:00
Luck 07514000a4 Fix global groups not being migrated from GroupManager 2016-09-09 18:41:15 +01:00
Luck f28721e55a Possible fix for permissions not applying on BungeeCord 2016-09-09 18:32:50 +01:00
Luck fc230291d3 Fix ops not receiving all permission nodes 2016-09-09 18:00:38 +01:00
Luck 89ff4cfa40 Add a much better system for exporting data 2016-09-08 21:01:37 +01:00
Luck d786b6abb2 Fix removing negative nodes 2016-09-08 17:28:31 +01:00
Luck fb0e694155 Add auto-op. Closes #8 2016-09-07 23:21:24 +01:00
Luck cd6cac1fb6 Fix bad grammar in config option comment 2016-09-07 21:01:03 +01:00
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
151 changed files with 7618 additions and 1827 deletions

No files matched your search

+10 -1
View File
@@ -1,5 +1,6 @@
![alt text](https://i.imgur.com/7TwJZ5e.png "Banner")
# LuckPerms [![Build Status](https://ci.lucko.me/job/LuckPerms/badge/icon)](https://ci.lucko.me/job/LuckPerms/)
A permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
An advanced permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
## Links
* **Development Builds** - <https://ci.lucko.me/job/LuckPerms>
@@ -9,9 +10,17 @@ A permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
## Description
LuckPerms is an advanced permissions implementation aiming to be a flexible, performant and reliable alternative to other permission systems, providing all the features you could ever possibly need in a permissions plugin, along with an extensive and well documented API for developers.
* [Spigot Plugin Page](https://www.spigotmc.org/resources/luckperms-an-advanced-permissions-system.28140/ "Spigot Plugin Page")
* [Sponge Plugin Page](https://forums.spongepowered.org/t/luckperms-an-advanced-permissions-system/14274 "Sponge Plugin Page")
## LuckPerms vs Other Plugins
![alt text](https://static.lucko.me/luckperms-compare-copy.png "Feature comparison")
_Features checked on 23rd Aug 2016. If you find any inaccuracies, please do let me know. I tried to be as fair as possible, and copy all major features from the respective plugin pages._
## License
See LICENSE.md.
## Thanks to:
[![JProfiler](https://www.ej-technologies.com/images/product_banners/jprofiler_large.png)](http://www.ej-technologies.com/products/jprofiler/overview.html)
Thanks to ej-technologies for granting LuckPerms an open source licence to their [java profiling software](http://www.ej-technologies.com/products/jprofiler/overview.html "Java Profiler").
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.9-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();
@@ -60,6 +62,24 @@ public interface LPConfiguration {
*/
boolean getIncludeGlobalPerms();
/**
* @return if the users on this server will have their global world permissions applied
* @since 2.9
*/
boolean getIncludeGlobalWorldPerms();
/**
* @return true if the platform is applying global groups
* @since 2.9
*/
boolean getApplyGlobalGroups();
/**
* @return true if the platform is applying global world groups
* @since 2.9
*/
boolean getApplyGlobalWorldGroups();
/**
* @return the online mode setting in the config
*/
@@ -80,6 +100,48 @@ 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 permission checks are being recorded / debugged
* @since 2.9
*/
boolean getDebugPermissionChecks();
/**
* @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 true if auto op is enabled
* @since 2.9
*/
boolean getAutoOp();
/**
* @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 +160,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);
@@ -181,7 +187,7 @@ public interface LuckPermsApi {
Track getTrack(String name);
/**
* Gets a wrapped tracj object from the track storage. This method does not return null, unlike {@link #getTrack}
* Gets a wrapped track object from the track storage. This method does not return null, unlike {@link #getTrack}
* @param name the name of the track to get
* @return an optional {@link Track} object
* @throws NullPointerException if the name is null
@@ -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,49 @@
/*
* 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"),
/**
* @since 2.9
*/
STANDALONE("Standalone");
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,30 @@
/*
* 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.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 {
}
+119
View File
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luckperms-bukkit-legacy</artifactId>
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPerms-bukkit-legacy-${release.version}.${git.closest.tag.commit.count}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<artifactSet>
<excludes>
<exclude>org.xerial:*</exclude>
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>me.lucko.luckperms.lib.slf4j</shadedPattern>
</relocation>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>com.mongodb</pattern>
<shadedPattern>me.lucko.luckperms.lib.mongodb</shadedPattern>
</relocation>
<relocation>
<pattern>org.bson</pattern>
<shadedPattern>me.lucko.luckperms.lib.bson</shadedPattern>
</relocation>
<relocation>
<pattern>org.h2</pattern>
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<injectAllReactorProjects>true</injectAllReactorProjects>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- LuckPerms Bukkit -->
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-bukkit</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- Shade gson and guava, as they are not included in earlier Bukkit releases -->
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.7</version>
<scope>compile</scope>
</dependency>
<!-- guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
+3 -3
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,8 +30,8 @@
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<version>2.8</version>
<scope>provided</scope>
</dependency>
<!-- BukkitAPI -->
<dependency>
@@ -25,15 +25,11 @@ package me.lucko.luckperms.api.placeholders;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.util.TimeUtil;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.Track;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.*;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
/*
* PlaceholderAPI Expansion for LuckPerms, implemented using the LuckPerms API.
@@ -51,7 +47,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 +71,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
@Override
public String getVersion() {
return api == null ? "null" : api.getVersion();
return "2.8";
}
@Override
@@ -85,7 +80,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
return "";
}
Optional<User> u = api.getUserSafe(player.getUniqueId());
Optional<User> u = api.getUserSafe(api.getUuidCache().getUUID(player.getUniqueId()));
if (!u.isPresent()) {
return "";
}
@@ -128,17 +123,11 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
if (identifier.startsWith("expiry_time_") && identifier.length() > "expiry_time_".length()) {
String node = identifier.substring("expiry_time_".length());
long currentTime = System.currentTimeMillis() / 1000L;
Map<Map.Entry<String, Boolean>, Long> temps = user.getTemporaryNodes();
for (Map.Entry<Map.Entry<String, Boolean>, Long> e : temps.entrySet()) {
if (!e.getKey().getKey().equalsIgnoreCase(node)) {
continue;
for (Node n : user.getTemporaryPermissionNodes()) {
if (n.getPermission().equalsIgnoreCase(node)) {
return TimeUtil.getTime((int) (n.getExpiryUnixTime() - currentTime));
}
return TimeUtil.getTime((int) (e.getValue() - currentTime));
}
return "";
@@ -146,42 +135,27 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
if (identifier.startsWith("group_expiry_time_") && identifier.length() > "group_expiry_time_".length()) {
String node = "group." + identifier.substring("group_expiry_time_".length());
long currentTime = System.currentTimeMillis() / 1000L;
Map<Map.Entry<String, Boolean>, Long> temps = user.getTemporaryNodes();
for (Map.Entry<Map.Entry<String, Boolean>, Long> e : temps.entrySet()) {
if (!e.getKey().getKey().equalsIgnoreCase(node)) {
continue;
for (Node n : user.getTemporaryPermissionNodes()) {
if (n.getPermission().equalsIgnoreCase(node)) {
return TimeUtil.getTime((int) (n.getExpiryUnixTime() - currentTime));
}
return TimeUtil.getTime((int) (e.getValue() - currentTime));
}
return "";
}
if (identifier.equalsIgnoreCase("prefix")) {
return getChatMeta(PREFIX_PATTERN, user);
return MetaUtils.getPrefix(user, null, null, true);
}
if (identifier.equalsIgnoreCase("suffix")) {
return getChatMeta(SUFFIX_PATTERN, user);
return MetaUtils.getSuffix(user, null, null, true);
}
if (identifier.startsWith("meta_") && identifier.length() > "meta_".length()) {
String node = "meta." + escapeCharacters(identifier.substring("meta_".length())) + ".";
Map<String, Boolean> nodes = user.getNodes();
for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
if (!e.getValue()) continue;
if (!e.getKey().toLowerCase().startsWith(node)) continue;
String meta = e.getKey().substring(node.length());
return unescapeCharacters(meta);
}
return "";
String node = identifier.substring("meta_".length());
return MetaUtils.getMeta(user, null, null, node, "", true);
}
return null;
@@ -205,44 +179,4 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
private static String formatBoolean(boolean b) {
return b ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse();
}
private static String escapeCharacters(String s) {
s = s.replace(".", "{SEP}");
s = s.replace("/", "{FSEP}");
s = s.replace("$", "{DSEP}");
return s;
}
private static String unescapeCharacters(String s) {
s = s.replace("{SEP}", ".");
s = s.replace("{FSEP}", "/");
s = s.replace("{DSEP}", "$");
return s;
}
private static final Pattern PREFIX_PATTERN = Pattern.compile("(?i)prefix\\.\\d+\\..*");
private static final Pattern SUFFIX_PATTERN = Pattern.compile("(?i)suffix\\.\\d+\\..*");
private static final Pattern DOT_PATTERN = Pattern.compile("\\.");
private static String getChatMeta(Pattern pattern, User user) {
int priority = 0;
String meta = null;
for (Map.Entry<String, Boolean> e : user.getLocalPermissions(null, null).entrySet()) {
if (!e.getValue()) continue;
if (pattern.matcher(e.getKey()).matches()) {
String[] parts = DOT_PATTERN.split(e.getKey(), 3);
int p = Integer.parseInt(parts[1]);
if (meta == null || p > priority) {
meta = parts[2];
priority = p;
}
}
}
return meta == null ? "" : unescapeCharacters(meta);
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -22,8 +22,12 @@
package me.lucko.luckperms;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Patterns;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -39,7 +43,12 @@ class BukkitCommand extends CommandManager implements CommandExecutor, TabExecut
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
onCommand(BukkitSenderFactory.get().wrap(sender), label, Arrays.asList(args), Callback.empty());
onCommand(
BukkitSenderFactory.get().wrap(sender),
label,
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args))),
Callback.empty()
);
return true;
}
@@ -23,16 +23,18 @@
package me.lucko.luckperms;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.inject.Injector;
import me.lucko.luckperms.inject.LPPermissible;
import me.lucko.luckperms.users.BukkitUser;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.AbstractListener;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.*;
import org.bukkit.permissions.PermissionAttachment;
import java.util.concurrent.ConcurrentHashMap;
import java.util.UUID;
class BukkitListener extends AbstractListener implements Listener {
private final LPBukkitPlugin plugin;
@@ -42,10 +44,10 @@ class BukkitListener extends AbstractListener implements Listener {
this.plugin = plugin;
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
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;
}
@@ -53,7 +55,7 @@ class BukkitListener extends AbstractListener implements Listener {
onAsyncLogin(e.getUniqueId(), e.getName());
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent e) {
final Player player = e.getPlayer();
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
@@ -66,36 +68,55 @@ class BukkitListener extends AbstractListener implements Listener {
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
PermissionAttachment attachment = player.addAttachment(plugin);
try {
BukkitUser.getPermissionsField().set(attachment, new ConcurrentHashMap<>());
LPPermissible lpPermissible = new LPPermissible(player, plugin);
Injector.inject(player, lpPermissible);
u.setLpPermissible(lpPermissible);
} catch (Throwable t) {
t.printStackTrace();
}
u.setAttachment(attachment);
}
plugin.doAsync(user::refreshPermissions);
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent e) {
// Refresh permissions again
plugin.getUserManager().getWorldCache().put(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(e.getPlayer().getUniqueId()));
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
plugin.getUserManager().getWorldCache().put(internal, e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(internal));
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerChangedWorld(PlayerChangedWorldEvent e) {
plugin.getUserManager().getWorldCache().put(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(e.getPlayer().getUniqueId()));
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
plugin.getUserManager().getWorldCache().put(internal, e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(internal));
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerQuit(PlayerQuitEvent e) {
plugin.getUserManager().getWorldCache().remove(e.getPlayer().getUniqueId());
plugin.getUserManager().getWorldCache().remove(plugin.getUuidCache().getUUID(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());
}
}
}
@@ -34,8 +34,8 @@ import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BukkitSenderFactory extends SenderFactory<CommandSender> {
private static BukkitSenderFactory instance = null;
public static BukkitSenderFactory get() {
if (instance == null){
public static synchronized BukkitSenderFactory get() {
if (instance == null) {
instance = new BukkitSenderFactory();
}
return instance;
@@ -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
@@ -186,8 +194,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
}
@Override
public List<Sender> getSenders() {
return getServer().getOnlinePlayers().stream().map(p -> BukkitSenderFactory.get().wrap(p)).collect(Collectors.toList());
public List<Sender> getNotifyListeners() {
return getServer().getOnlinePlayers().stream()
.map(p -> BukkitSenderFactory.get().wrap(p, Collections.singleton(me.lucko.luckperms.constants.Permission.LOG_NOTIFY)))
.filter(me.lucko.luckperms.constants.Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
}
@Override
@@ -217,6 +228,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 +247,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 -> true))
)
);
}
pm.addPermission(new Permission("luckperms.*", def, all.stream().collect(Collectors.toMap(s -> s, s -> true))));
}
}
@@ -30,8 +30,10 @@ import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.Contexts;
import net.milkbowl.vault.chat.Chat;
import java.util.Iterator;
import java.util.Map;
import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters;
@@ -80,113 +82,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,60 +119,71 @@ public class VaultChatHook extends Chat {
continue;
}
if (!n.shouldApplyOnWorld(world, true, false)) {
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)) {
for (Node n : holder.getAllNodes(null, Contexts.allowAll())) {
if (!n.getValue()) {
continue;
}
if (!n.shouldApplyOnWorld(world, true, false)) {
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
if (prefix) {
if (!n.isPrefix()) {
continue;
}
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
Map.Entry<Integer, String> prefixValue = n.getPrefix();
if (prefixValue.getKey() > priority) {
meta = prefixValue.getValue();
priority = prefixValue.getKey();
}
} else {
if (!n.isSuffix()) {
continue;
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
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 +197,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 +207,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 +217,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 +227,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 +246,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 +260,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 +274,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 +288,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 +302,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]);
}
@@ -0,0 +1,108 @@
/*
* 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.inject;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.PermissibleBase;
import java.lang.reflect.Field;
/**
* Injects a {@link LPPermissible} into a {@link Player}
*/
@UtilityClass
public class Injector {
private static Field HUMAN_ENTITY_FIELD;
static {
try {
HUMAN_ENTITY_FIELD = Class.forName(getInternalClassName("entity.CraftHumanEntity")).getDeclaredField("perm");
HUMAN_ENTITY_FIELD.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean inject(CommandSender sender, PermissibleBase permissible) {
try {
Field f = getPermField(sender);
f.set(sender, permissible);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static boolean unInject(CommandSender sender) {
try {
Permissible permissible = getPermissible(sender);
if (permissible instanceof LPPermissible) {
getPermField(sender).set(sender, new PermissibleBase(sender));
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static Permissible getPermissible(CommandSender sender) {
try {
Field f = getPermField(sender);
return (Permissible) f.get(sender);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Field getPermField(CommandSender sender) {
if (sender instanceof Player) {
return HUMAN_ENTITY_FIELD;
}
throw new RuntimeException("Couldn't get perm field for sender " + sender.getClass().getName());
}
private static String getInternalClassName(String className) {
Class server = Bukkit.getServer().getClass();
if (!server.getSimpleName().equals("CraftServer")) {
throw new RuntimeException("Couldn't inject into server " + server);
}
String version;
if (server.getName().equals("org.bukkit.craftbukkit.CraftServer")) {
// Non versioned class
version = ".";
} else {
version = server.getName().substring("org.bukkit.craftbukkit".length());
version = version.substring(0, version.length() - "CraftServer".length());
}
return "org.bukkit.craftbukkit" + version + className;
}
}
@@ -0,0 +1,303 @@
/*
* 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.inject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.utils.PermissionCalculator;
import me.lucko.luckperms.utils.PermissionProcessor;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.*;
import org.bukkit.plugin.Plugin;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
/**
* Modified PermissibleBase for LuckPerms
*/
public class LPPermissible extends PermissibleBase {
@Getter
private final CommandSender parent;
private final PermissionCalculator calculator;
@Getter
private final Map<String, Boolean> luckPermsPermissions = new ConcurrentHashMap<>();
private final List<PermissionAttachment> attachments = new LinkedList<>();
private final Map<String, PermissionAttachmentInfo> attachmentPermissions = new HashMap<>();
public LPPermissible(@NonNull CommandSender sender, LuckPermsPlugin plugin) {
super(sender);
this.parent = sender;
List<PermissionProcessor> processors = new ArrayList<>(5);
processors.add(new PermissionCalculator.MapProcessor(luckPermsPermissions));
processors.add(new AttachmentProcessor(attachmentPermissions));
if (plugin.getConfiguration().getApplyWildcards()) {
processors.add(new PermissionCalculator.WildcardProcessor(luckPermsPermissions));
}
if (plugin.getConfiguration().getApplyRegex()) {
processors.add(new PermissionCalculator.RegexProcessor(luckPermsPermissions));
}
processors.add(new BukkitDefaultsProcessor(parent::isOp));
calculator = new PermissionCalculator(plugin, parent.getName(), plugin.getConfiguration().getDebugPermissionChecks(), processors);
}
public void invalidateCache() {
calculator.invalidateCache();
}
@Override
public boolean isOp() {
return parent.isOp();
}
@Override
public void setOp(boolean value) {
parent.setOp(value);
}
@Override
public boolean isPermissionSet(@NonNull String name) {
return luckPermsPermissions.containsKey(name.toLowerCase()) || attachmentPermissions.containsKey(name.toLowerCase());
}
@Override
public boolean isPermissionSet(@NonNull Permission perm) {
return isPermissionSet(perm.getName());
}
@Override
public boolean hasPermission(@NonNull String name) {
Tristate ts = calculator.getPermissionValue(name);
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
return Permission.DEFAULT_PERMISSION.getValue(isOp());
}
@Override
public boolean hasPermission(@NonNull Permission perm) {
Tristate ts = calculator.getPermissionValue(perm.getName());
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
return perm.getDefault().getValue(isOp());
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = addAttachment(plugin);
result.setPermission(name, value);
recalculatePermissions();
return result;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = new PermissionAttachment(plugin, parent);
attachments.add(result);
recalculatePermissions();
return result;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value, int ticks) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = addAttachment(plugin, ticks);
if (result != null) {
result.setPermission(name, value);
}
return result;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, int ticks) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = addAttachment(plugin);
if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new RemoveAttachmentRunnable(result), ticks) == -1) {
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not add PermissionAttachment to " + parent + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1");
result.remove();
return null;
} else {
return result;
}
}
@Override
public void removeAttachment(@NonNull PermissionAttachment attachment) {
if (attachments.contains(attachment)) {
attachments.remove(attachment);
PermissionRemovedExecutor ex = attachment.getRemovalCallback();
if (ex != null) {
ex.attachmentRemoved(attachment);
}
recalculatePermissions();
} else {
throw new IllegalArgumentException("Given attachment is not part of Permissible object " + parent);
}
}
@Override
public void recalculatePermissions() {
if (attachmentPermissions == null) {
return;
}
attachmentPermissions.clear();
Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(isOp());
Bukkit.getServer().getPluginManager().subscribeToDefaultPerms(isOp(), parent);
for (Permission perm : defaults) {
String name = perm.getName().toLowerCase();
attachmentPermissions.put(name, new PermissionAttachmentInfo(parent, name, null, true));
Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent);
calculateChildPermissions(perm.getChildren(), false, null);
}
for (PermissionAttachment attachment : attachments) {
calculateChildPermissions(attachment.getPermissions(), false, attachment);
}
invalidateCache();
}
@Override
public synchronized void clearPermissions() {
Set<String> perms = attachmentPermissions.keySet();
for (String name : perms) {
Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, parent);
}
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(false, parent);
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(true, parent);
attachmentPermissions.clear();
}
private void calculateChildPermissions(Map<String, Boolean> children, boolean invert, PermissionAttachment attachment) {
for (Map.Entry<String, Boolean> e : children.entrySet()) {
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
boolean value = e.getValue() ^ invert;
String name = e.getKey().toLowerCase();
attachmentPermissions.put(name, new PermissionAttachmentInfo(parent, name, attachment, value));
Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent);
if (perm != null) {
calculateChildPermissions(perm.getChildren(), !value, attachment);
}
}
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
perms.addAll(attachmentPermissions.values());
perms.addAll(luckPermsPermissions.entrySet().stream()
.map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue()))
.collect(Collectors.toList()));
return perms;
}
private class RemoveAttachmentRunnable implements Runnable {
private PermissionAttachment attachment;
private RemoveAttachmentRunnable(PermissionAttachment attachment) {
this.attachment = attachment;
}
public void run() {
attachment.remove();
}
}
@AllArgsConstructor
private static class AttachmentProcessor implements PermissionProcessor {
@Getter
private final Map<String, PermissionAttachmentInfo> map;
@Override
public Tristate hasPermission(String permission) {
if (map.containsKey(permission)) {
return Tristate.fromBoolean(map.get(permission).getValue());
}
return Tristate.UNDEFINED;
}
}
@AllArgsConstructor
private static class BukkitDefaultsProcessor implements PermissionProcessor {
private final Supplier<Boolean> isOp;
@Override
public Tristate hasPermission(String permission) {
Permission defPerm = Bukkit.getServer().getPluginManager().getPermission(permission);
if (defPerm != null) {
return Tristate.fromBoolean(defPerm.getDefault().getValue(isOp.get()));
} else {
return Tristate.UNDEFINED;
}
}
}
}
@@ -27,32 +27,20 @@ import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
import me.lucko.luckperms.api.implementation.internal.UserLink;
import org.bukkit.permissions.PermissionAttachment;
import me.lucko.luckperms.inject.LPPermissible;
import me.lucko.luckperms.utils.Contexts;
import org.bukkit.permissions.Permissible;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
public class BukkitUser extends User {
private static Field permissionsField = null;
public static Field getPermissionsField() {
if (permissionsField == null) {
try {
permissionsField = PermissionAttachment.class.getDeclaredField("permissions");
permissionsField.setAccessible(true);
} catch (SecurityException | NoSuchFieldException e) {
e.printStackTrace();
}
}
return permissionsField;
}
private final LPBukkitPlugin plugin;
@Getter
@Setter
private PermissionAttachment attachment = null;
private LPPermissible lpPermissible = null;
BukkitUser(UUID uuid, LPBukkitPlugin plugin) {
super(uuid, plugin);
@@ -66,17 +54,28 @@ public class BukkitUser extends User {
@SuppressWarnings("unchecked")
@Override
public void refreshPermissions() {
if (attachment == null) {
public synchronized void refreshPermissions() {
if (lpPermissible == null) {
return;
}
// 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(
new Contexts(
getPlugin().getConfiguration().getServer(),
plugin.getUserManager().getWorldCache().get(getUuid()),
null,
getPlugin().getConfiguration().getIncludeGlobalPerms(),
getPlugin().getConfiguration().getIncludeGlobalWorldPerms(),
true,
getPlugin().getConfiguration().getApplyGlobalGroups(),
getPlugin().getConfiguration().getApplyGlobalWorldGroups()
),
Collections.emptyList()
);
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);
Map<String, Boolean> existing = lpPermissible.getLuckPermsPermissions();
boolean different = false;
if (toApply.size() != existing.size()) {
@@ -94,17 +93,30 @@ public class BukkitUser extends User {
if (!different) return;
existing.clear();
lpPermissible.invalidateCache();
existing.putAll(toApply);
/* 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());
if (plugin.getConfiguration().getAutoOp()) {
boolean op = false;
for (Map.Entry<String, Boolean> e : toApply.entrySet()) {
if (e.getKey().equalsIgnoreCase("luckperms.autoop") && e.getValue()) {
op = true;
break;
}
}
final boolean finalOp = op;
if (lpPermissible.isOp() != op) {
final Permissible parent = lpPermissible.getParent();
plugin.doSync(() -> parent.setOp(finalOp));
}
}
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
} catch (Exception e) {
e.printStackTrace();
}
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
}
}
@@ -24,6 +24,7 @@ package me.lucko.luckperms.users;
import lombok.Getter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.inject.Injector;
import org.bukkit.entity.Player;
import java.util.Map;
@@ -44,23 +45,20 @@ 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;
Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid()));
if (player != null) {
if (u.getLpPermissible() != null) {
Injector.unInject(player);
u.setLpPermissible(null);
}
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
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 (plugin.getConfiguration().getAutoOp()) {
player.setOp(false);
}
}
getAll().remove(user.getUuid());
}
}
+59 -6
View File
@@ -8,9 +8,20 @@
# The name of the server, used for server specific permissions. Set to 'global' to disable.
server: global
# If users on this server should have their global permissions/groups applied.
# If users on this server should have their global permissions applied.
# If set to false, only server specific permissions will apply for users on this server
include-global: true
# If users on this server should have their global world permissions applied.
# If set to false, only world specific permissions will apply for users on this server
include-global-world: true
# If users on this server should have global (non-server specific) groups applied
apply-global-groups: true
# If users on this server should have global (non-world specific) groups applied
apply-global-world-groups: true
# If this server is in offline or online mode.
# This setting allows a player to have the same UUID across a network of offline mode/mixed servers.
@@ -22,28 +33,70 @@ 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.
# If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered permissions matching
# the wildcard. This will only work for plugins that define all of their permissions to the server.
# the wildcard.
apply-wildcards: true
# If the plugin should parse regex permissions.
# If set to true, LuckPerms will detect regex permissions, marked with "r=" at the start of the node, and resolve &
# apply all registered permissions matching the regex. This will only work for plugins that define all of their
# permissions to the server.
# apply all registered permissions matching the regex.
apply-regex: true
# If the plugin should complete and apply shorthand permissions.
# 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 LuckPerms should print to console every time a plugin checks if a player has a permission
debug-permission-checks: false
# 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 set to true, any user with the permission "luckperms.autoop" will automatically be granted server operator status.
# This permission can be inherited, or set on specific servers/worlds, temporarily, etc.
# Additionally, setting this to true will force the "enable-ops" option above to false. All users will be de-opped unless
# they have the permission node, and the op/deop commands will be disabled.
#
# It is recommended that you use this option instead of assigning a single '*' permission.
auto-op: false
# If opped players should be allowed to use LuckPerms commands. Set to false to only allow users who have the permissions access to the commands
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
@@ -52,4 +105,4 @@ data:
# Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing.
# e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources.
sync-minutes: 3
sync-minutes: 3
+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.9-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>
@@ -22,8 +22,12 @@
package me.lucko.luckperms;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Patterns;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
@@ -40,7 +44,12 @@ class BungeeCommand extends Command implements TabExecutor {
@Override
public void execute(CommandSender sender, String[] args) {
manager.onCommand(BungeeSenderFactory.get().wrap(sender), "bperms", Arrays.asList(args), Callback.empty());
manager.onCommand(
BungeeSenderFactory.get().wrap(sender),
"bperms",
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args))),
Callback.empty()
);
}
@Override
@@ -33,9 +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;
import java.util.concurrent.TimeUnit;
@@ -52,22 +49,18 @@ public class BungeeListener extends AbstractListener implements Listener {
@EventHandler
public void onPlayerPermissionCheck(PermissionCheckEvent e) {
if (!(e.getSender() instanceof ProxiedPlayer)) {
e.setHasPermission(true);
return;
}
final ProxiedPlayer player = ((ProxiedPlayer) e.getSender());
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (user == null) return;
final String server = player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName());
Map<String, Boolean> local = user.getLocalPermissions(plugin.getConfiguration().getServer(), server, null, Collections.singletonList(e.getPermission()));
for (Map.Entry<String, Boolean> en : local.entrySet()) {
if (en.getKey().equalsIgnoreCase(e.getPermission())) {
e.setHasPermission(en.getValue());
return;
}
BungeePlayerCache playerCache = plugin.getPlayerCache().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (playerCache == null) {
return;
}
e.setHasPermission(playerCache.getPermissionValue(e.getPermission()));
}
@EventHandler
@@ -109,23 +102,20 @@ 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 UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
final User user = plugin.getUserManager().get(internal);
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 {
plugin.getPlayerCache().put(internal, new BungeePlayerCache(plugin, e.getPlayer().getName()));
user.refreshPermissions();
}
}
@EventHandler
public void onPlayerQuit(PlayerDisconnectEvent e) {
plugin.getPlayerCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
onLeave(e.getPlayer().getUniqueId());
}
@@ -0,0 +1,61 @@
/*
* 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;
import lombok.Getter;
import me.lucko.luckperms.utils.PermissionCalculator;
import me.lucko.luckperms.utils.PermissionProcessor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BungeePlayerCache {
private final PermissionCalculator calculator;
@Getter
private final Map<String, Boolean> permissions = new ConcurrentHashMap<>();
public BungeePlayerCache(LuckPermsPlugin plugin, String name) {
List<PermissionProcessor> processors = new ArrayList<>(3);
processors.add(new PermissionCalculator.MapProcessor(permissions));
if (plugin.getConfiguration().getApplyWildcards()) {
processors.add(new PermissionCalculator.WildcardProcessor(permissions));
}
if (plugin.getConfiguration().getApplyRegex()) {
processors.add(new PermissionCalculator.RegexProcessor(permissions));
}
calculator = new PermissionCalculator(plugin, name, plugin.getConfiguration().getDebugPermissionChecks(), processors);
}
public void invalidateCache() {
calculator.invalidateCache();
}
public boolean getPermissionValue(String permission) {
return calculator.getPermissionValue(permission).asBoolean();
}
}
@@ -35,8 +35,8 @@ import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BungeeSenderFactory extends SenderFactory<CommandSender> {
private static BungeeSenderFactory instance = null;
public static BungeeSenderFactory get() {
if (instance == null){
public static synchronized BungeeSenderFactory get() {
if (instance == null) {
instance = new BungeeSenderFactory();
}
return instance;
@@ -24,11 +24,13 @@ 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;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.data.Importer;
@@ -45,16 +47,14 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Getter
public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
private final Map<UUID, BungeePlayerCache> playerCache = new ConcurrentHashMap<>();
private final Set<UUID> ignoringLogs = ConcurrentHashMap.newKeySet();
private LPConfiguration configuration;
private UserManager userManager;
@@ -135,8 +135,8 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
}
@Override
public Type getType() {
return Type.BUNGEE;
public PlatformType getType() {
return PlatformType.BUNGEE;
}
@Override
@@ -160,8 +160,11 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
}
@Override
public List<Sender> getSenders() {
return getProxy().getPlayers().stream().map(p -> BungeeSenderFactory.get().wrap(p)).collect(Collectors.toList());
public List<Sender> getNotifyListeners() {
return getProxy().getPlayers().stream()
.map(p -> BungeeSenderFactory.get().wrap(p, Collections.singleton(Permission.LOG_NOTIFY)))
.filter(Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
}
@Override
@@ -185,6 +188,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()
@@ -22,21 +22,80 @@
package me.lucko.luckperms.users;
import me.lucko.luckperms.BungeePlayerCache;
import me.lucko.luckperms.LPBungeePlugin;
import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
import me.lucko.luckperms.api.implementation.internal.UserLink;
import me.lucko.luckperms.utils.Contexts;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
public class BungeeUser extends User {
private final LPBungeePlugin plugin;
BungeeUser(UUID uuid, LPBungeePlugin plugin) {
super(uuid, plugin);
this.plugin = plugin;
}
BungeeUser(UUID uuid, String username, LPBungeePlugin plugin) {
super(uuid, username, plugin);
this.plugin = plugin;
}
@Override
public void refreshPermissions() {
// Do nothing. Permissions are applied when needed in a listener.
public synchronized void refreshPermissions() {
ProxiedPlayer player = plugin.getProxy().getPlayer(plugin.getUuidCache().getExternalUUID(getUuid()));
if (player == null) {
return;
}
BungeePlayerCache playerCache = plugin.getPlayerCache().get(getUuid());
if (playerCache == null) {
return;
}
final String server = player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName());
// Calculate the permissions that should be applied. This is done async.
Map<String, Boolean> toApply = exportNodes(
new Contexts(
plugin.getConfiguration().getServer(),
server,
null,
plugin.getConfiguration().getIncludeGlobalPerms(),
plugin.getConfiguration().getIncludeGlobalWorldPerms(),
true,
plugin.getConfiguration().getApplyGlobalGroups(),
plugin.getConfiguration().getApplyGlobalWorldGroups()
),
Collections.emptyList()
);
Map<String, Boolean> existing = playerCache.getPermissions();
boolean different = false;
if (toApply.size() != existing.size()) {
different = true;
} else {
for (Map.Entry<String, Boolean> e : existing.entrySet()) {
if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) {
continue;
}
different = true;
break;
}
}
if (!different) return;
existing.clear();
playerCache.invalidateCache();
existing.putAll(toApply);
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
}
}
@@ -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) {
+37 -5
View File
@@ -8,9 +8,20 @@
# The name of the server, used for server specific permissions. Set to 'global' to disable.
server: bungee
# If users on this server should have their global permissions/groups applied.
# If users on this server should have their global permissions applied.
# If set to false, only server specific permissions will apply for users on this server
include-global: false
# If users on this server should have their global world permissions applied.
# If set to false, only world specific permissions will apply for users on this server
include-global-world: true
# If users on this server should have global (non-server specific) groups applied
apply-global-groups: true
# If users on this server should have global (non-world specific) groups applied
apply-global-world-groups: true
# If this server is in offline or online mode.
# This setting allows a player to have the same UUID across a network of offline mode/mixed servers.
@@ -22,28 +33,49 @@ 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.
# If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered permissions matching
# the wildcard. This will only work for plugins that define all of their permissions to the server.
# the wildcard.
apply-wildcards: true
# If the plugin should parse regex permissions.
# If set to true, LuckPerms will detect regex permissions, marked with "r=" at the start of the node, and resolve &
# apply all registered permissions matching the regex. This will only work for plugins that define all of their
# permissions to the server.
# apply all registered permissions matching the regex.
apply-regex: true
# If the plugin should complete and apply shorthand permissions.
# 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 LuckPerms should print to console every time a plugin checks if a player has a permission
debug-permission-checks: false
# 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.9-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
@@ -103,7 +103,7 @@ public interface LuckPermsPlugin {
/**
* @return a {@link List} of senders online on the platform
*/
List<Sender> getSenders();
List<Sender> getNotifyListeners();
/**
* @return the console sender of the instance
@@ -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.9;
}
@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}
*/
@@ -59,6 +61,21 @@ public class LPConfigurationLink implements LPConfiguration {
return master.getIncludeGlobalPerms();
}
@Override
public boolean getIncludeGlobalWorldPerms() {
return master.getIncludeGlobalWorldPerms();
}
@Override
public boolean getApplyGlobalGroups() {
return master.getApplyGlobalGroups();
}
@Override
public boolean getApplyGlobalWorldGroups() {
return master.getApplyGlobalWorldGroups();
}
@Override
public boolean getOnlineMode() {
return master.getOnlineMode();
@@ -79,6 +96,41 @@ public class LPConfigurationLink implements LPConfiguration {
return master.getApplyShorthand();
}
@Override
public boolean getLogNotify() {
return master.getLogNotify();
}
@Override
public boolean getDebugPermissionChecks() {
return master.getDebugPermissionChecks();
}
@Override
public boolean getEnableOps() {
return master.getEnableOps();
}
@Override
public boolean getCommandsAllowOp() {
return master.getCommandsAllowOp();
}
@Override
public boolean getAutoOp() {
return master.getAutoOp();
}
@Override
public String getVaultServer() {
return master.getVaultServer();
}
@Override
public boolean getVaultIncludeGlobal() {
return master.getVaultIncludeGlobal();
}
@SuppressWarnings("deprecation")
@Override
public MySQLConfiguration getDatabaseValues() {
@@ -94,4 +146,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();
}
}
@@ -29,6 +29,7 @@ import me.lucko.luckperms.api.PermissionHolder;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.utils.Contexts;
import java.util.*;
@@ -52,7 +53,7 @@ public class PermissionHolderLink implements PermissionHolder {
@Override
public SortedSet<Node> getPermissions() {
return Collections.unmodifiableSortedSet(master.getPermissions());
return Collections.unmodifiableSortedSet(master.getPermissions(false));
}
@Override
@@ -67,7 +68,7 @@ public class PermissionHolderLink implements PermissionHolder {
@Override
public Set<Node> getAllNodes() {
return Collections.unmodifiableSet(master.getAllNodes(null));
return Collections.unmodifiableSet(master.getAllNodes(Collections.emptyList(), Contexts.allowAll()));
}
@Override
@@ -252,7 +253,7 @@ public class PermissionHolderLink implements PermissionHolder {
@Override
public Map<String, Boolean> getPermissions(String server, String world, Map<String, String> extraContext, boolean includeGlobal, List<String> possibleNodes, boolean applyGroups) {
return master.exportNodes(server, world, extraContext, includeGlobal, applyGroups, possibleNodes);
return master.exportNodes(new Contexts(server, world, extraContext, includeGlobal, includeGlobal, applyGroups, true, true), possibleNodes);
}
@Override
@@ -61,6 +61,7 @@ public class CommandManager {
.add(new InfoCommand())
.add(new DebugCommand())
.add(new ImportCommand())
.add(new ExportCommand())
.add(new QueueCommand())
.add(new MigrationMainCommand())
.add(new CreateGroup())
@@ -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,78 @@ 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);
return new SenderImp(t, null);
}
// 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);
}
}
};
public final Sender wrap(T t, Set<Permission> toCheck) {
return new SenderImp(t, toCheck);
}
@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, Set<Permission> toCheck) {
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) {
if (toCheck == null || toCheck.isEmpty()) {
this.perms = ImmutableMap.copyOf(Arrays.stream(Permission.values())
.collect(Collectors.toMap(p -> p, p -> factory.hasPermission(t, p.getNode()))));
} else {
this.perms = ImmutableMap.copyOf(toCheck.stream().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 {
@@ -38,6 +37,22 @@ public class Util {
sender.sendMessage(color(Message.PREFIX + message));
}
public static List<String> stripQuotes(List<String> input) {
input = new ArrayList<>(input);
ListIterator<String> iterator = input.listIterator();
while (iterator.hasNext()) {
String value = iterator.next();
if (!(value.length() >= 3)) {
continue;
}
if (value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') {
iterator.set(value.substring(1, value.length() - 1));
}
}
return input;
}
public static String color(String s) {
return translateAlternateColorCodes('&', s);
}
@@ -114,35 +129,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 +194,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,90 @@
/*
* 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 me.lucko.luckperms.utils.Contexts;
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, Contexts.allowAll())) {
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;
}
}
}
@@ -28,7 +28,6 @@ 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.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
@@ -53,7 +52,7 @@ public class GroupSetPermission extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.GROUP_USE_INHERIT.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ 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.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
@@ -55,7 +54,7 @@ public class GroupSetTempPermission extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.GROUP_USE_INHERIT.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ 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.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -52,7 +51,7 @@ public class GroupUnSetPermission extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.GROUP_USE_UNINHERIT.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ 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.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -53,7 +52,7 @@ public class GroupUnsetTempPermission extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.GROUP_USE_UNINHERIT.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -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,253 @@
/*
* 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.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();
}
}
}
}
}
@@ -123,6 +123,7 @@ public class MigrationGroupManager extends SubCommand<Object> {
}
}
plugin.getDatastore().saveGroup(group);
}
Map<UUID, Map<Map.Entry<String, String>, Boolean>> users = new HashMap<>();
@@ -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);
@@ -141,7 +118,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
for (String node : group.getOwnPermissions(null)) {
boolean value = true;
if (node.startsWith("!")) {
if (node.startsWith("-")) {
node = node.substring(1);
value = false;
}
@@ -166,7 +143,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
for (String world : worlds) {
for (String node : group.getOwnPermissions(world)) {
boolean value = true;
if (node.startsWith("!")) {
if (node.startsWith("-")) {
node = node.substring(1);
value = false;
}
@@ -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());
}
}
@@ -292,7 +262,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
for (String node : user.getOwnPermissions(null)) {
boolean value = true;
if (node.startsWith("!")) {
if (node.startsWith("-")) {
node = node.substring(1);
value = false;
}
@@ -317,7 +287,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
for (String world : worlds) {
for (String node : user.getOwnPermissions(world)) {
boolean value = true;
if (node.startsWith("!")) {
if (node.startsWith("-")) {
node = node.substring(1);
value = false;
}
@@ -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);
}
@@ -23,14 +23,17 @@
package me.lucko.luckperms.commands.migration.subcommands;
import com.github.cheesesoftware.PowerfulPermsAPI.*;
import com.google.common.util.concurrent.ListenableFuture;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.data.Callback;
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.commands.migration.subcommands.utils.LPResultRunnable;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.data.LogEntry;
@@ -45,39 +48,72 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import static me.lucko.luckperms.constants.Permission.MIGRATION;
@SuppressWarnings("unchecked")
public class MigrationPowerfulPerms extends SubCommand<Object> {
/* <sadness>
The PowerfulPerms API is a complete joke. Seriously, it would probably be easier reflecting into the actual plugin.
Methods move about randomly every version...
What kind of API requires reflection to function with multiple versions...
Doesn't that just defeat the whole god damn point of having an API in the first place?
Whatever happened to the concept of depreciation and responsible API creation?
I tried to keep reflection to a minimum, but in some places there's no other option.
This class is a complete fucking mess for that reason. I sad now :(
</sadness> */
Whatever happened to depreciation?
</sadness> */
private static Method getPlayerPermissionsMethod = null;
private static Method getPlayerGroupsMethod = null;
private static Method getGroupMethod = null;
// lol
private static boolean superLegacy = false;
private static boolean legacy = false;
static {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerOwnGroups", UUID.class, ResultRunnable.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
Class.forName("com.github.cheesesoftware.PowerfulPermsAPI.ResponseRunnable");
legacy = true;
} catch (ClassNotFoundException ignored) {}
if (legacy) {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerGroups", UUID.class, ResultRunnable.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
}
getPlayerPermissionsMethod = PermissionManager.class.getMethod("getPlayerOwnPermissions", UUID.class, ResultRunnable.class);
getPlayerPermissionsMethod.setAccessible(true);
} catch (NoSuchMethodException ignored) {}
} else {
try {
getPlayerPermissionsMethod = PermissionManager.class.getMethod("getPlayerOwnPermissions", UUID.class);
getPlayerPermissionsMethod.setAccessible(true);
} catch (NoSuchMethodException ignored) {}
}
try {
getGroupMethod = CachedGroup.class.getMethod("getGroup");
getGroupMethod.setAccessible(true);
legacy = true;
} catch (NoSuchMethodException ignored) {
superLegacy = true;
} catch (NoSuchMethodException ignored) {}
if (!legacy) {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerOwnGroups", UUID.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
} else {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerOwnGroups", UUID.class, ResultRunnable.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
try {
getPlayerGroupsMethod = PermissionManager.class.getMethod("getPlayerGroups", UUID.class, ResultRunnable.class);
getPlayerGroupsMethod.setAccessible(true);
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
}
}
}
}
@@ -97,6 +133,42 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
}
}
private static void getPlayerPermissions(PermissionManager manager, UUID uuid, Callback<List<Permission>> callback) {
if (legacy) {
try {
getPlayerPermissionsMethod.invoke(manager, uuid, new LPResultRunnable<List<Permission>>() {
@Override
public void run() {
callback.onComplete(getResult());
}
});
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
} else {
try {
ListenableFuture<List<Permission>> lf = (ListenableFuture<List<Permission>>) getPlayerPermissionsMethod.invoke(manager, uuid);
try {
if (lf.isDone()) {
callback.onComplete(lf.get());
} else {
lf.addListener(() -> {
try {
callback.onComplete(lf.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}, Runnable::run);
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
private CommandResult run(LuckPermsPlugin plugin, List<String> args) {
final Logger log = plugin.getLog();
if (!plugin.isPluginLoaded("PowerfulPerms")) {
@@ -168,7 +240,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
// Groups first.
log.info("PowerfulPerms Migration: Starting group migration.");
Map<Integer, Group> groups = pm.getGroups();
Map<Integer, Group> groups = pm.getGroups(); // All versions
for (Group g : groups.values()) {
plugin.getDatastore().createAndLoadGroup(g.getName().toLowerCase());
final me.lucko.luckperms.groups.Group group = plugin.getGroupManager().get(g.getName().toLowerCase());
@@ -181,16 +253,16 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
ex.printStackTrace();
}
for (Permission p : g.getOwnPermissions()) {
for (Permission p : g.getOwnPermissions()) { // All versions
applyPerm(group, p, plugin);
}
for (Group parent : g.getParents()) {
for (Group parent : g.getParents()) { // All versions
try {
group.setPermission("group." + parent.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(group).action("setinherit " + parent.getName().toLowerCase())
.acted(group).action("setinherit " + parent.getName().toLowerCase()) // All versions
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
@@ -216,156 +288,176 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
User user = plugin.getUserManager().get(uuid);
// Get a list of Permissions held by the user from the PP API.
pm.getPlayerOwnPermissions(uuid, new LPResultRunnable<List<Permission>>() {
@Override
public void run() {
List<Permission> perms = this.getResult();
perms.forEach(p -> applyPerm(user, p, plugin));
getPlayerPermissions(pm, uuid, perms -> { // Changes each version
perms.forEach(p -> applyPerm(user, p, plugin));
// Update the progress so the user can be saved and unloaded.
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getUserManager().cleanup(user);
}
// Update the progress so the user can be saved and unloaded.
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getUserManager().cleanup(user);
}
}
});
// Migrate the user's groups to LuckPerms from PP.
try {
getPlayerGroupsMethod.invoke(pm, uuid, new LPResultRunnable<LinkedHashMap<String, List<CachedGroup>>>() {
@Override
public void run() {
Map<String, List<CachedGroup>> groups = getResult();
for (Map.Entry<String, List<CachedGroup>> e : groups.entrySet()) {
final String server;
if (e.getKey() != null && (e.getKey().equals("") || e.getKey().equalsIgnoreCase("all"))) {
server = null;
} else {
server = e.getKey();
}
// This is horrible. So many random API changes through versions, no depreciation.
if (legacy) {
e.getValue().stream()
.filter(cg -> !cg.isNegated())
.map(cg -> {
try {
return (Group) getGroupMethod.invoke(cg);
} catch (IllegalAccessException | InvocationTargetException e1) {
e1.printStackTrace();
return null;
}
})
.forEach(g -> {
if (g != null) {
if (server == null) {
try {
user.setPermission("group." + g.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + g.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + g.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + g.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
});
} else {
e.getValue().stream()
.filter(g -> !g.hasExpired() && !g.isNegated())
.forEach(g -> {
final Group group = pm.getGroup(g.getGroupId());
if (g.willExpire()) {
if (server == null) {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
} else {
if (server == null) {
try {
user.setPermission("group." + group.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + group.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + group.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
});
}
}
// Update the progress so the user can be saved and unloaded.
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getUserManager().cleanup(user);
}
}
Callback<Map<String, List<CachedGroup>>> callback = groups1 -> {
for (Map.Entry<String, List<CachedGroup>> e : groups1.entrySet()) {
final String server;
if (e.getKey() != null && (e.getKey().equals("") || e.getKey().equalsIgnoreCase("all"))) {
server = null;
} else {
server = e.getKey();
}
});
} catch (IllegalAccessException | InvocationTargetException e) {
log.info("PowerfulPerms Migration: Error");
e.printStackTrace();
if (superLegacy) {
e.getValue().stream()
.filter(cg -> !cg.isNegated())
.map(cg -> {
try {
return (Group) getGroupMethod.invoke(cg);
} catch (IllegalAccessException | InvocationTargetException e1) {
e1.printStackTrace();
return null;
}
})
.forEach(g -> {
if (g != null) {
if (server == null) {
try {
user.setPermission("group." + g.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + g.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + g.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + g.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
});
} else {
e.getValue().stream()
.filter(g -> !g.hasExpired() && !g.isNegated())
.forEach(g -> {
final Group group = pm.getGroup(g.getGroupId());
if (g.willExpire()) {
if (server == null) {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
} else {
if (server == null) {
try {
user.setPermission("group." + group.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + group.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
} else {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(user).action("addgroup " + group.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
});
}
}
// Update the progress so the user can be saved and unloaded.
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getUserManager().cleanup(user);
}
}
};
if (!legacy) {
try {
ListenableFuture<LinkedHashMap<String, List<CachedGroup>>> future = (ListenableFuture<LinkedHashMap<String, List<CachedGroup>>>) getPlayerGroupsMethod.invoke(pm, uuid);
try {
if (future.isDone()) {
callback.onComplete(future.get());
} else {
future.addListener(() -> {
try {
callback.onComplete(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}, Runnable::run);
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
} catch (IllegalAccessException | InvocationTargetException e) {
log.info("PowerfulPerms Migration: Error");
e.printStackTrace();
}
} else {
try {
getPlayerGroupsMethod.invoke(pm, uuid, new LPResultRunnable<LinkedHashMap<String, List<CachedGroup>>>() {
@Override
public void run() {
callback.onComplete(getResult());
}
});
} catch (IllegalAccessException | InvocationTargetException e) {
log.info("PowerfulPerms Migration: Error");
e.printStackTrace();
}
}
}
@@ -417,7 +509,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
}
long expireAt = 0L;
if (!legacy) {
if (!superLegacy) {
if (p.willExpire()) {
expireAt = p.getExpirationDate().getTime() / 1000L;
}
@@ -508,22 +600,4 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
}
}
}
/**
* Overrides the default ResultRunnable, callbacks will always run in the same thread. (an async one, hopefully.)
* @param <T> type
*/
@SuppressWarnings("WeakerAccess")
public abstract class LPResultRunnable<T> extends ResultRunnable<T> {
public LPResultRunnable() {
super();
super.sameThread = true;
}
public T getResult() {
return super.result;
}
}
}
@@ -0,0 +1,41 @@
/*
* 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.migration.subcommands.utils;
import com.github.cheesesoftware.PowerfulPermsAPI.ResultRunnable;
/**
* Overrides the default ResultRunnable, callbacks will always run in the same thread. (an async one, hopefully.)
* @param <T> type
*/
public abstract class LPResultRunnable<T> extends ResultRunnable<T> {
public LPResultRunnable() {
super();
super.sameThread = true;
}
public T getResult() {
return super.result;
}
}
@@ -38,6 +38,7 @@ public class DebugCommand extends SingleMainCommand {
@Override
protected CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) {
plugin.getLog().info(sender.getName() + " used the debug command.");
Message.DEBUG.send(sender,
plugin.getPlayerCount(),
plugin.getUserManager().getAll().size(),
@@ -0,0 +1,209 @@
/*
* 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.misc;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SingleMainCommand;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
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 java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class ExportCommand extends SingleMainCommand {
public ExportCommand() {
super("Export", "/%s export <file>", 1, Permission.MIGRATION);
}
@Override
protected CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) {
final Logger log = plugin.getLog();
if (!sender.getUuid().equals(Constants.getConsoleUUID())) {
Message.MIGRATION_NOT_CONSOLE.send(sender);
return CommandResult.NO_PERMISSION;
}
File f = new File(plugin.getMainDir(), args.get(0));
if (f.exists()) {
Message.LOG_EXPORT_ALREADY_EXISTS.send(sender, f.getAbsolutePath());
return CommandResult.INVALID_ARGS;
}
try {
f.createNewFile();
} catch (IOException e) {
Message.LOG_EXPORT_FAILURE.send(sender);
e.printStackTrace();
return CommandResult.FAILURE;
}
if (!Files.isWritable(f.toPath())) {
Message.LOG_EXPORT_NOT_WRITABLE.send(sender, f.getAbsolutePath());
return CommandResult.FAILURE;
}
try (FileWriter fWriter = new FileWriter(f, true); BufferedWriter writer = new BufferedWriter(fWriter)) {
log.info("Export: Starting export process.");
// Export Groups
log.info("Export: Exporting all groups.");
// Create the groups first
for (Group group : plugin.getGroupManager().getAll().values()) {
write(writer, "/luckperms creategroup " + group.getName());
}
int groupCount = 0;
for (Group group : plugin.getGroupManager().getAll().values()) {
groupCount++;
for (Node node : group.getNodes()) {
write(writer, nodeToString(node, group.getName(), true));
}
}
log.info("Export: Exported " + groupCount + " groups.");
// Export tracks
log.info("Export: Exporting all tracks.");
// Create the tracks first
for (Track track : plugin.getTrackManager().getAll().values()) {
write(writer, "/luckperms createtrack " + track.getName());
}
int trackCount = 0;
for (Track track : plugin.getTrackManager().getAll().values()) {
trackCount++;
for (String group : track.getGroups()) {
write(writer, "/luckperms track " + track.getName() + " append " + group);
}
}
log.info("Export: Exported " + trackCount + " tracks.");
// Export users
log.info("Export: Exporting all users. Finding a list of unique users to export.");
Datastore ds = plugin.getDatastore();
Set<UUID> users = ds.getUniqueUsers();
log.info("Export: Found " + users.size() + " unique users to export.");
int userCount = 0;
for (UUID uuid : users) {
userCount++;
plugin.getDatastore().loadUser(uuid, "null");
User user = plugin.getUserManager().get(uuid);
for (Node node : user.getNodes()) {
if (node.isGroupNode() && node.getGroupName().equalsIgnoreCase("default")) {
continue;
}
write(writer, nodeToString(node, user.getUuid().toString(), false));
}
write(writer, "/luckperms user " + user.getUuid().toString() + " setprimarygroup " + user.getPrimaryGroup());
plugin.getUserManager().cleanup(user);
}
log.info("Export: Exported " + userCount + " users.");
try {
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
Message.LOG_EXPORT_SUCCESS.send(sender, f.getAbsolutePath());
return CommandResult.SUCCESS;
} catch (Throwable t) {
t.printStackTrace();
return CommandResult.FAILURE;
}
}
@Override
protected boolean isAuthorized(Sender sender) {
return sender.getUuid().equals(Constants.getConsoleUUID());
}
private static void write(BufferedWriter writer, String s) {
try {
writer.write(s);
writer.newLine();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String nodeToString(Node node, String id, boolean group) {
StringBuilder sb = new StringBuilder();
sb.append("/luckperms ").append(group ? "group " : "user ").append(id).append(" ");
if (node.isGroupNode()) {
if (node.isTemporary()) {
sb.append(group ? "settempinherit " : "addtempgroup ");
sb.append(node.getGroupName());
sb.append(" ").append(node.getExpiryUnixTime());
} else {
sb.append(group ? "setinherit " : "addgroup ");
sb.append(node.getGroupName());
}
if (node.isWorldSpecific()) {
sb.append(" ").append(node.getServer().get()).append(" ").append(node.getWorld().get());
} else if (node.isServerSpecific()) {
sb.append(" ").append(node.getServer().get());
}
return sb.toString();
}
sb.append(node.isTemporary() ? "settemp " : "set ");
sb.append(node.getPermission()).append(" ").append(node.getValue());
if (node.isTemporary()) {
sb.append(" ").append(node.getExpiryUnixTime());
}
if (node.isWorldSpecific()) {
sb.append(" ").append(node.getServer().get()).append(" ").append(node.getWorld().get());
} else if (node.isServerSpecific()) {
sb.append(" ").append(node.getServer().get());
}
return sb.toString();
}
}
@@ -44,10 +44,14 @@ public class InfoCommand extends SingleMainCommand {
final LPConfiguration c = plugin.getConfiguration();
Message.INFO.send(sender,
plugin.getVersion(),
plugin.getType().getFriendlyName(),
plugin.getDatastore().getName(),
c.getServer(),
c.getSyncTime(),
formatBoolean(c.getIncludeGlobalPerms()),
formatBoolean(c.getIncludeGlobalWorldPerms()),
formatBoolean(c.getApplyGlobalGroups()),
formatBoolean(c.getApplyGlobalWorldGroups()),
formatBoolean(c.getOnlineMode()),
formatBoolean(c.getApplyWildcards()),
formatBoolean(c.getApplyRegex()),
@@ -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,94 @@
/*
* 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 me.lucko.luckperms.utils.Contexts;
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, Contexts.allowAll())) {
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;
}
}
}
@@ -28,7 +28,6 @@ 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.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
@@ -54,7 +53,7 @@ public class UserSetPermission extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.USER_USE_ADDGROUP.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -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());
@@ -28,7 +28,6 @@ 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.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
@@ -55,7 +54,7 @@ public class UserSetTempPermission extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.USER_USE_ADDGROUP.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ 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.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -52,7 +51,7 @@ public class UserUnSetPermission extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.USER_USE_REMOVEGROUP.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -28,7 +28,6 @@ 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.Patterns;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -53,7 +52,7 @@ public class UserUnsetTempPermission extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (Patterns.GROUP_MATCH.matcher(node).matches()) {
if (node.toLowerCase().startsWith("group.")) {
Message.USER_USE_REMOVEGROUP.send(sender);
return CommandResult.INVALID_ARGS;
}
@@ -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),
@@ -122,10 +123,14 @@ public enum Message {
UPDATE_TASK_RUN("&bRunning update task for all online users.", true),
INFO(
PREFIX + "&6Running &bLuckPerms v%s&6 by &bLuck&6." + "\n" +
PREFIX + "&f-> &ePlatform: &6%s" + "\n" +
PREFIX + "&f-> &eStorage Method: &6%s" + "\n" +
PREFIX + "&f-> &eServer Name: &6%s" + "\n" +
PREFIX + "&f-> &eSync Interval: &6%s minutes" + "\n" +
PREFIX + "&f-> &eInclude Global Perms: &6%s" + "\n" +
PREFIX + "&f-> &eInclude Global: &6%s" + "\n" +
PREFIX + "&f-> &eInclude Global World: &6%s" + "\n" +
PREFIX + "&f-> &eApply Global Groups: &6%s" + "\n" +
PREFIX + "&f-> &eApply Global World Groups: &6%s" + "\n" +
PREFIX + "&f-> &eOnline Mode: &6%s" + "\n" +
PREFIX + "&f-> &eApply Wildcards: &6%s" + "\n" +
PREFIX + "&f-> &eApply Regex: &6%s" + "\n" +
@@ -168,6 +173,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 +240,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),
@@ -291,6 +336,7 @@ public enum Message {
IMPORT_END_ERROR_HEADER(
PREFIX + "&e(Import) &7------------> &6Showing Error #&e%s &7<------------" + "\n" +
PREFIX + "&e(Import) &6Whilst executing: &fCommand #%s" + "\n" +
PREFIX + "&e(Import) &6Command: &7%s" + "\n" +
PREFIX + "&e(Import) &6Type: &f%s" + "\n" +
PREFIX + "&e(Import) &6Output:",
false
Loaded 100 of 151 files, more files were not shown because too many files have changed in this diff. Show more