Release 2.7

This commit is contained in:
Luck 2016-09-05 20:00:01 +01:00
parent ba04fb320b
commit 60bcd5f643
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
53 changed files with 676 additions and 284 deletions

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version> <version>2.7-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -28,7 +28,7 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
/** /**
* Wrapper interface for the internal Datastore instance * Interface for the internal Datastore instance
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public interface Datastore { public interface Datastore {
@ -55,7 +55,7 @@ public interface Datastore {
Async async(); 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}. * 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. * 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.) * @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 * @return if the operation was performed successfully
* @throws NullPointerException if uuid or username is null * @throws NullPointerException if uuid or username is null
* @since 1.6 * @since 2.6
*/ */
boolean loadUser(UUID uuid, String username); 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. * Removes users from the datastore who are "default". This is called every time the plugin loads.
* @return true if the operation completed successfully * @return true if the operation completed successfully
* @since 1.6 * @since 2.6
*/ */
boolean cleanupUsers(); boolean cleanupUsers();
/** /**
* Gets a set all user's UUIDs who are "unique", and aren't just a member of the "default" group. * 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. * @return a set of uuids, or null if the operation failed.
* @since 1.6 * @since 2.6
*/ */
Set<UUID> getUniqueUsers(); Set<UUID> getUniqueUsers();

View File

@ -28,7 +28,7 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List; import java.util.List;
/** /**
* Wrapper interface for internal Group instances * Interface for internal Group instances
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public interface Group extends PermissionHolder { public interface Group extends PermissionHolder {

View File

@ -25,6 +25,8 @@ package me.lucko.luckperms.api;
import me.lucko.luckperms.api.data.DatastoreConfiguration; import me.lucko.luckperms.api.data.DatastoreConfiguration;
import me.lucko.luckperms.api.data.MySQLConfiguration; import me.lucko.luckperms.api.data.MySQLConfiguration;
import java.util.Map;
/** /**
* A wrapper interface for the internal LuckPerms configuration, providing read only access. * 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 * @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 @Deprecated
String getDefaultGroupNode(); String getDefaultGroupNode();
/** /**
* @return the name of the default group * @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 @Deprecated
String getDefaultGroupName(); String getDefaultGroupName();
@ -80,6 +82,24 @@ public interface LPConfiguration {
*/ */
boolean getApplyShorthand(); boolean getApplyShorthand();
/**
* @return if LuckPerms will send notifications to users when permissions are modified
* @since 2.7
*/
boolean getLogNotify();
/**
* @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 * @return the database values set in the configuration
* @deprecated use {@link #getDatastoreConfig()} * @deprecated use {@link #getDatastoreConfig()}
@ -98,4 +118,17 @@ public interface LPConfiguration {
*/ */
String getStorageMethod(); 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();
} }

View File

@ -23,7 +23,7 @@
package me.lucko.luckperms.api; 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 * <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. * module can access a logger.

View File

@ -41,7 +41,7 @@ public interface LuckPermsApi {
/** /**
* @return the version of the API running on the platform * @return the version of the API running on the platform
* @since 1.6 * @since 2.6
*/ */
double getApiVersion(); double getApiVersion();
@ -50,6 +50,11 @@ public interface LuckPermsApi {
*/ */
String getVersion(); String getVersion();
/**
* @return the platform LuckPerms is running on
*/
PlatformType getPlatformType();
/** /**
* Registers a listener to be sent LuckPerms events * Registers a listener to be sent LuckPerms events
* @param listener the listener instance * @param listener the listener instance
@ -138,7 +143,7 @@ public interface LuckPermsApi {
* Unload a user from the internal storage, if they're not currently online. * Unload a user from the internal storage, if they're not currently online.
* @param user the user to unload * @param user the user to unload
* @throws NullPointerException if the user is null * @throws NullPointerException if the user is null
* @since 1.6 * @since 2.6
*/ */
void cleanupUser(User user); void cleanupUser(User user);
@ -208,7 +213,7 @@ public interface LuckPermsApi {
* @return a {@link Node.Builder} instance * @return a {@link Node.Builder} instance
* @throws IllegalArgumentException if the permission is invalid * @throws IllegalArgumentException if the permission is invalid
* @throws NullPointerException if the permission is null * @throws NullPointerException if the permission is null
* @since 1.6 * @since 2.6
*/ */
Node.Builder buildNode(String permission) throws IllegalArgumentException; Node.Builder buildNode(String permission) throws IllegalArgumentException;

View File

@ -0,0 +1,314 @@
/*
* 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 {
/**
* 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);
}
}

View File

@ -30,7 +30,7 @@ import java.util.Optional;
/** /**
* Represents an immutable node object * Represents an immutable node object
* <p> Use {@link LuckPermsApi#buildNode(String)} to get an instance. * <p> Use {@link LuckPermsApi#buildNode(String)} to get an instance.
* @since 1.6 * @since 2.6
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public interface Node extends Map.Entry<String, Boolean> { public interface Node extends Map.Entry<String, Boolean> {
@ -260,6 +260,9 @@ public interface Node extends Map.Entry<String, Boolean> {
*/ */
boolean almostEquals(Node node); boolean almostEquals(Node node);
/**
* Builds a Node instance
*/
interface Builder { interface Builder {
Builder setNegated(boolean negated); Builder setNegated(boolean negated);
Builder setValue(boolean value); Builder setValue(boolean value);

View File

@ -31,7 +31,7 @@ import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
/** /**
* Wrapper interface for internal PermissionHolder (user/group) instances * Interface for internal PermissionHolder (user/group) instances
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public interface PermissionHolder { public interface PermissionHolder {
@ -45,21 +45,21 @@ public interface PermissionHolder {
/** /**
* Gets an immutable Set of the objects permission nodes * Gets an immutable Set of the objects permission nodes
* @return an immutable set of permissions in priority order * @return an immutable set of permissions in priority order
* @since 1.6 * @since 2.6
*/ */
SortedSet<Node> getPermissions(); SortedSet<Node> getPermissions();
/** /**
* Similar to {@link #getPermissions()}, except excluding transient permissions * Similar to {@link #getPermissions()}, except excluding transient permissions
* @return a set of nodes * @return a set of nodes
* @since 1.6 * @since 2.6
*/ */
Set<Node> getEnduringPermissions(); Set<Node> getEnduringPermissions();
/** /**
* Similar to {@link #getPermissions()}, except excluding non-transient permissions * Similar to {@link #getPermissions()}, except excluding non-transient permissions
* @return a set of nodes * @return a set of nodes
* @since 1.6 * @since 2.6
*/ */
Set<Node> getTransientPermissions(); Set<Node> getTransientPermissions();
@ -67,7 +67,7 @@ public interface PermissionHolder {
/** /**
* Gets an immutable set of the nodes that this object has and inherits * Gets an immutable set of the nodes that this object has and inherits
* @return an immutable set of permissions * @return an immutable set of permissions
* @since 1.6 * @since 2.6
*/ */
Set<Node> getAllNodes(); Set<Node> getAllNodes();
@ -84,7 +84,7 @@ public interface PermissionHolder {
* @param node the node to check for * @param node the node to check for
* @return a Tristate for the holders permission status for the node * @return a Tristate for the holders permission status for the node
* @throws NullPointerException if the node is null * @throws NullPointerException if the node is null
* @since 1.6 * @since 2.6
*/ */
Tristate hasPermission(Node node); Tristate hasPermission(Node node);
@ -93,7 +93,7 @@ public interface PermissionHolder {
* @param node the node to check for * @param node the node to check for
* @return a Tristate for the holders permission status for the node * @return a Tristate for the holders permission status for the node
* @throws NullPointerException if the node is null * @throws NullPointerException if the node is null
* @since 1.6 * @since 2.6
*/ */
Tristate hasTransientPermission(Node node); Tristate hasTransientPermission(Node node);
@ -171,7 +171,7 @@ public interface PermissionHolder {
* @param node the node to check for * @param node the node to check for
* @return a Tristate for the holders inheritance status for the node * @return a Tristate for the holders inheritance status for the node
* @throws NullPointerException if the node is null * @throws NullPointerException if the node is null
* @since 1.6 * @since 2.6
*/ */
Tristate inheritsPermission(Node node); Tristate inheritsPermission(Node node);
@ -249,7 +249,7 @@ public interface PermissionHolder {
* @param node The node to be set * @param node The node to be set
* @throws ObjectAlreadyHasException if the object already has the permission * @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null * @throws NullPointerException if the node is null
* @since 1.6 * @since 2.6
*/ */
void setPermission(Node node) throws ObjectAlreadyHasException; void setPermission(Node node) throws ObjectAlreadyHasException;
@ -267,7 +267,7 @@ public interface PermissionHolder {
* @param node The node to be set * @param node The node to be set
* @throws ObjectAlreadyHasException if the object already has the permission * @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null * @throws NullPointerException if the node is null
* @since 1.6 * @since 2.6
*/ */
void setTransientPermission(Node node) throws ObjectAlreadyHasException; void setTransientPermission(Node node) throws ObjectAlreadyHasException;
@ -357,7 +357,7 @@ public interface PermissionHolder {
* @param node The node to be unset * @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set * @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null * @throws NullPointerException if the node is null
* @since 1.6 * @since 2.6
*/ */
void unsetPermission(Node node) throws ObjectLacksException; void unsetPermission(Node node) throws ObjectLacksException;
@ -366,7 +366,7 @@ public interface PermissionHolder {
* @param node The node to be unset * @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set * @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null * @throws NullPointerException if the node is null
* @since 1.6 * @since 2.6
*/ */
void unsetTransientPermission(Node node) throws ObjectLacksException; void unsetTransientPermission(Node node) throws ObjectLacksException;
@ -498,7 +498,7 @@ public interface PermissionHolder {
* @param possibleNodes a list of possible permissions for resolving wildcards * @param possibleNodes a list of possible permissions for resolving wildcards
* @param applyGroups if inherited group permissions should be included * @param applyGroups if inherited group permissions should be included
* @return a map of permissions * @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); 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. * Processes the nodes and returns the temporary ones.
* @return a set of temporary nodes * @return a set of temporary nodes
* @since 1.6 * @since 2.6
*/ */
Set<Node> getTemporaryPermissionNodes(); Set<Node> getTemporaryPermissionNodes();
@ -528,7 +528,7 @@ public interface PermissionHolder {
/** /**
* Processes the nodes and returns the non-temporary ones. * Processes the nodes and returns the non-temporary ones.
* @return a set of permanent nodes * @return a set of permanent nodes
* @since 1.6 * @since 2.6
*/ */
Set<Node> getPermanentPermissionNodes(); Set<Node> getPermanentPermissionNodes();

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
/**
* Represents the platform type that LuckPerms is running on
* @since 2.7
*/
public enum PlatformType {
BUKKIT("Bukkit"),
BUNGEE("Bungee"),
SPONGE("Sponge");
private final String friendlyName;
PlatformType(String friendlyName) {
this.friendlyName = friendlyName;
}
public String getFriendlyName() {
return friendlyName;
}
}

View File

@ -28,7 +28,7 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List; import java.util.List;
/** /**
* Wrapper interface for internal Track instances * Interface for internal Track instances
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public interface Track { public interface Track {

View File

@ -29,7 +29,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
/** /**
* Wrapper interface for internal User instances * Interface for internal User instances
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public interface User extends PermissionHolder { public interface User extends PermissionHolder {

View File

@ -25,7 +25,9 @@ package me.lucko.luckperms.api;
import java.util.UUID; 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 * 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 * 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. * the datastore cache, to get a UUID for a user that is consistent across an entire network.

View File

@ -26,7 +26,7 @@ import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.PermissionHolder; import me.lucko.luckperms.api.PermissionHolder;
/** /**
* @since 1.6 * @since 2.6
*/ */
public class AbstractPermissionEvent extends TargetedEvent<PermissionHolder> { public class AbstractPermissionEvent extends TargetedEvent<PermissionHolder> {

View File

@ -28,7 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/** /**
* Called when a temporary permission node expires * Called when a temporary permission node expires
* @since 1.6 * @since 2.6
*/ */
public class PermissionNodeExpireEvent extends AbstractPermissionEvent { public class PermissionNodeExpireEvent extends AbstractPermissionEvent {
public PermissionNodeExpireEvent(PermissionHolder target, Node node) { public PermissionNodeExpireEvent(PermissionHolder target, Node node) {

View File

@ -28,7 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/** /**
* Called when a permission node is set on a holder * Called when a permission node is set on a holder
* @since 1.6 * @since 2.6
*/ */
public class PermissionNodeSetEvent extends AbstractPermissionEvent { public class PermissionNodeSetEvent extends AbstractPermissionEvent {
public PermissionNodeSetEvent(PermissionHolder target, Node node) { public PermissionNodeSetEvent(PermissionHolder target, Node node) {

View File

@ -28,7 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/** /**
* Called when a permission node is unset from a holder * Called when a permission node is unset from a holder
* @since 1.6 * @since 2.6
*/ */
public class PermissionNodeUnsetEvent extends AbstractPermissionEvent { public class PermissionNodeUnsetEvent extends AbstractPermissionEvent {
public PermissionNodeUnsetEvent(PermissionHolder target, Node node) { public PermissionNodeUnsetEvent(PermissionHolder target, Node node) {

View File

@ -0,0 +1,8 @@
package me.lucko.luckperms.exceptions;
/**
* Thrown when a permission holding object doesn't / already has a permission or isn't / is already is a member of a group
* @since 2.7
*/
public abstract class MembershipException extends Exception {
}

View File

@ -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 * 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. * already contains a group.
*/ */
public class ObjectAlreadyHasException extends Exception { public class ObjectAlreadyHasException extends MembershipException {
} }

View File

@ -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, * 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. * or when a track doesn't contain a group.
*/ */
public class ObjectLacksException extends Exception { public class ObjectLacksException extends MembershipException {
} }

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version> <version>2.7-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -30,7 +30,7 @@
<dependency> <dependency>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-api</artifactId> <artifactId>luckperms-api</artifactId>
<version>${project.version}</version> <version>2.5</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<!-- BukkitAPI --> <!-- BukkitAPI -->

View File

@ -51,7 +51,6 @@ import java.util.regex.Pattern;
* - suffix * - suffix
* - meta_<node> * - meta_<node>
*/ */
@SuppressWarnings("deprecation")
public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion { public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
private static final String IDENTIFIER = "luckperms"; private static final String IDENTIFIER = "luckperms";
private static final String PLUGIN_NAME = "LuckPerms"; private static final String PLUGIN_NAME = "LuckPerms";
@ -76,7 +75,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
@Override @Override
public String getVersion() { public String getVersion() {
return api == null ? "null" : api.getVersion(); return "2.5";
} }
@Override @Override

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version> <version>2.7-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -46,7 +46,7 @@ class BukkitListener extends AbstractListener implements Listener {
@EventHandler @EventHandler
public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) { public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) {
if (!plugin.getDatastore().isAcceptingLogins()) { 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()); e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.toString());
return; return;
} }
@ -70,6 +70,11 @@ class BukkitListener extends AbstractListener implements Listener {
PermissionAttachment attachment = player.addAttachment(plugin); PermissionAttachment attachment = player.addAttachment(plugin);
Map<String, Boolean> newPermMap = new ConcurrentHashMap<>(); Map<String, Boolean> newPermMap = new ConcurrentHashMap<>();
try { try {
/* Replace the standard LinkedHashMap in the attachment with a ConcurrentHashMap.
This means that we can iterate over and change the permissions within our attachment asynchronously,
without worrying about thread safety. The Bukkit side of things should still operate normally. Internal
permission stuff should work the same. This is by far the most easy and efficient way to do things, without
having to do tons of reflection. */
BukkitUser.getPermissionsField().set(attachment, newPermMap); BukkitUser.getPermissionsField().set(attachment, newPermMap);
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();

View File

@ -25,6 +25,7 @@ package me.lucko.luckperms;
import lombok.Getter; import lombok.Getter;
import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.LuckPermsApi; import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.api.vault.VaultHook; import me.lucko.luckperms.api.vault.VaultHook;
import me.lucko.luckperms.commands.ConsecutiveExecutor; import me.lucko.luckperms.commands.ConsecutiveExecutor;
@ -161,8 +162,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
} }
@Override @Override
public Type getType() { public PlatformType getType() {
return Type.BUKKIT; return PlatformType.BUKKIT;
} }
@Override @Override

View File

@ -32,6 +32,7 @@ import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
import net.milkbowl.vault.chat.Chat; import net.milkbowl.vault.chat.Chat;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters; import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters;
@ -80,108 +81,30 @@ public class VaultChatHook extends Chat {
node = escapeCharacters(node); node = escapeCharacters(node);
value = escapeCharacters(value); value = escapeCharacters(value);
if (world == null || world.equals("")) { Iterator<Node> nodes = holder.getNodes().iterator();
try { while (nodes.hasNext()) {
holder.setPermission("meta." + node + "." + value, true); Node n = nodes.next();
} catch (ObjectAlreadyHasException ignored) {} if (n.isMeta() && n.getMeta().getKey().equals(node)) {
nodes.remove();
} else {
try {
holder.setPermission("meta." + node + "." + value, true, "global", world);
} catch (ObjectAlreadyHasException ignored) {}
} }
}
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); 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 (holder == null) return defaultValue;
if (node.equals("")) return defaultValue; if (node.equals("")) return defaultValue;
node = escapeCharacters(node); node = escapeCharacters(node);
@ -195,29 +118,50 @@ public class VaultChatHook extends Chat {
continue; continue;
} }
if (!n.shouldApplyOnWorld(world, true, false)) { if (!perms.getServer().equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
continue; continue;
} }
Map.Entry<String, String> meta = n.getMeta(); Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) { if (meta.getKey().equalsIgnoreCase(node)) {
return unescapeCharacters(meta.getValue());
}
}
return defaultValue;
}
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 { try {
return unescapeCharacters(meta.getValue()); holder.setPermission(node.build());
} catch (Throwable t) { } catch (ObjectAlreadyHasException ignored) {}
return defaultValue;
perms.objectSave(holder);
} }
} private String getChatMeta(boolean prefix, PermissionHolder holder, String world) {
}
return defaultValue;
}
private static String getChatMeta(boolean prefix, PermissionHolder holder, String world) {
if (holder == null) return ""; if (holder == null) return "";
int priority = -1000; int priority = Integer.MIN_VALUE;
String meta = null; String meta = null;
for (Node n : holder.getAllNodes(null)) { for (Node n : holder.getAllNodes(null)) {
@ -225,30 +169,24 @@ public class VaultChatHook extends Chat {
continue; continue;
} }
if (!n.shouldApplyOnWorld(world, true, false)) { if (!perms.getServer().equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
continue; continue;
} }
if (prefix) { if (prefix ? !n.isPrefix() : !n.isSuffix()) {
if (!n.isPrefix()) {
continue; continue;
} }
Map.Entry<Integer, String> prefixValue = n.getPrefix(); Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (prefixValue.getKey() > priority) { if (value.getKey() > priority) {
meta = prefixValue.getValue(); meta = value.getValue();
priority = prefixValue.getKey(); priority = value.getKey();
}
} else {
if (!n.isSuffix()) {
continue;
}
Map.Entry<Integer, String> suffixValue = n.getSuffix();
if (suffixValue.getKey() > priority) {
meta = suffixValue.getValue();
priority = suffixValue.getKey();
}
} }
} }
@ -262,15 +200,7 @@ public class VaultChatHook extends Chat {
public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) { public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) {
final User user = plugin.getUserManager().get(player); final User user = plugin.getUserManager().get(player);
if (user == null) return; setChatMeta(true, user, prefix, world);
if (prefix.equals("")) return;
try {
user.setPermission("prefix.1000." + escapeCharacters(prefix), true);
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(user);
} }
public String getPlayerSuffix(String world, @NonNull String player) { public String getPlayerSuffix(String world, @NonNull String player) {
@ -280,15 +210,7 @@ public class VaultChatHook extends Chat {
public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) { public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) {
final User user = plugin.getUserManager().get(player); final User user = plugin.getUserManager().get(player);
if (user == null) return; setChatMeta(false, user, suffix, world);
if (suffix.equals("")) return;
try {
user.setPermission("suffix.1000." + escapeCharacters(suffix), true);
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(user);
} }
public String getGroupPrefix(String world, @NonNull String group) { public String getGroupPrefix(String world, @NonNull String group) {
@ -298,15 +220,7 @@ public class VaultChatHook extends Chat {
public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) { public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) {
final Group g = plugin.getGroupManager().get(group); final Group g = plugin.getGroupManager().get(group);
if (g == null) return; setChatMeta(true, g, prefix, world);
if (prefix.equals("")) return;
try {
g.setPermission("prefix.1000." + escapeCharacters(prefix), true);
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(g);
} }
public String getGroupSuffix(String world, @NonNull String group) { public String getGroupSuffix(String world, @NonNull String group) {
@ -316,20 +230,16 @@ public class VaultChatHook extends Chat {
public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) { public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) {
final Group g = plugin.getGroupManager().get(group); final Group g = plugin.getGroupManager().get(group);
if (g == null) return; setChatMeta(false, g, suffix, world);
if (suffix.equals("")) return;
try {
g.setPermission("suffix.1000." + escapeCharacters(suffix), true);
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(g);
} }
public int getPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int defaultValue) { public int getPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int defaultValue) {
final User user = plugin.getUserManager().get(player); 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) { public void setPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int value) {
@ -339,7 +249,11 @@ public class VaultChatHook extends Chat {
public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) { public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) {
final Group g = plugin.getGroupManager().get(group); 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) { public void setGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int value) {
@ -349,7 +263,11 @@ public class VaultChatHook extends Chat {
public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) { public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) {
final User user = plugin.getUserManager().get(player); 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) { public void setPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double value) {
@ -359,7 +277,11 @@ public class VaultChatHook extends Chat {
public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) { public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) {
final Group g = plugin.getGroupManager().get(group); 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) { public void setGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double value) {
@ -369,7 +291,11 @@ public class VaultChatHook extends Chat {
public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) { public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) {
final User user = plugin.getUserManager().get(player); 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) { public void setPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean value) {
@ -379,7 +305,11 @@ public class VaultChatHook extends Chat {
public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) { public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) {
final Group g = plugin.getGroupManager().get(group); 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) { public void setGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean value) {

View File

@ -39,6 +39,8 @@ public class VaultHook {
permissionHook = new VaultPermissionHook(); permissionHook = new VaultPermissionHook();
} }
permissionHook.setPlugin(plugin); permissionHook.setPlugin(plugin);
permissionHook.setServer(plugin.getConfiguration().getVaultServer());
permissionHook.setIncludeGlobal(plugin.getConfiguration().getVaultIncludeGlobal());
if (chatHook == null) { if (chatHook == null) {
chatHook = new VaultChatHook(permissionHook); chatHook = new VaultChatHook(permissionHook);

View File

@ -22,6 +22,7 @@
package me.lucko.luckperms.api.vault; package me.lucko.luckperms.api.vault;
import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.Setter; import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin; import me.lucko.luckperms.LPBukkitPlugin;
@ -38,6 +39,14 @@ public class VaultPermissionHook extends Permission {
@Setter @Setter
private LPBukkitPlugin plugin; private LPBukkitPlugin plugin;
@Getter
@Setter
private String server = "global";
@Getter
@Setter
private boolean includeGlobal = true;
@Override @Override
public String getName() { public String getName() {
return "LuckPerms"; return "LuckPerms";
@ -57,9 +66,9 @@ public class VaultPermissionHook extends Permission {
if (object == null) return false; if (object == null) return false;
if (world != null && !world.equals("")) { if (world != null && !world.equals("")) {
return object.hasPermission(permission, true, "global", world); return object.hasPermission(permission, true, server, world);
} else { } else {
return object.hasPermission(permission, true); return object.hasPermission(permission, true, server);
} }
} }
@ -68,9 +77,9 @@ public class VaultPermissionHook extends Permission {
try { try {
if (world != null && !world.equals("")) { if (world != null && !world.equals("")) {
object.setPermission(permission, true, "global", world); object.setPermission(permission, true, server, world);
} else { } else {
object.setPermission(permission, true); object.setPermission(permission, true, server);
} }
} catch (ObjectAlreadyHasException ignored) {} } catch (ObjectAlreadyHasException ignored) {}
@ -83,9 +92,9 @@ public class VaultPermissionHook extends Permission {
try { try {
if (world != null && !world.equals("")) { if (world != null && !world.equals("")) {
object.unsetPermission(permission, "global", world); object.unsetPermission(permission, server, world);
} else { } else {
object.unsetPermission(permission); object.unsetPermission(permission, server);
} }
} catch (ObjectLacksException ignored) {} } catch (ObjectLacksException ignored) {}
@ -147,7 +156,7 @@ public class VaultPermissionHook extends Permission {
if (group1 == null) return false; if (group1 == null) return false;
if (world != null && !world.equals("")) { if (world != null && !world.equals("")) {
return user.isInGroup(group1, "global", world); return user.isInGroup(group1, server, world);
} else { } else {
return user.isInGroup(group1); return user.isInGroup(group1);
} }
@ -163,7 +172,7 @@ public class VaultPermissionHook extends Permission {
try { try {
if (world != null && !world.equals("")) { if (world != null && !world.equals("")) {
user.addGroup(group, "global", world); user.addGroup(group, server, world);
} else { } else {
user.addGroup(group); user.addGroup(group);
} }
@ -182,7 +191,7 @@ public class VaultPermissionHook extends Permission {
try { try {
if (world != null && !world.equals("")) { if (world != null && !world.equals("")) {
user.removeGroup(group, "global", world); user.removeGroup(group, server, world);
} else { } else {
user.removeGroup(group); user.removeGroup(group);
} }
@ -195,7 +204,7 @@ public class VaultPermissionHook extends Permission {
public String[] getPlayerGroups(String world, @NonNull String player) { public String[] getPlayerGroups(String world, @NonNull String player) {
final User user = plugin.getUserManager().get(player); final User user = plugin.getUserManager().get(player);
return (user == null) ? new String[0] : return (user == null) ? new String[0] :
world != null && !world.equals("") ? user.getGroups("global", world, true).toArray(new String[0]) : world != null && !world.equals("") ? user.getGroups(server, world, includeGlobal).toArray(new String[0]) :
user.getGroupNames().toArray(new String[0]); user.getGroupNames().toArray(new String[0]);
} }

View File

@ -84,7 +84,7 @@ public class BukkitUser extends User {
); );
try { try {
// The map in the LP PermissionAttachment is a ConcurrentHashMap. We can modify it's contents async. // The map in the LP PermissionAttachment is a ConcurrentHashMap. We can modify and iterate over its contents async.
Map<String, Boolean> existing = attachment.getPermissions(); Map<String, Boolean> existing = attachment.getPermissions();
boolean different = false; boolean different = false;

View File

@ -46,6 +46,13 @@ apply-shorthand: true
# If the plugin should send log notifications to users whenever permissions are modified. # If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true log-notify: true
# The name of the server used within Vault operations. If you don't want Vault operations to be server specific, set this
# to "global".
vault-server: global
# If global permissions should be considered when retrieving meta or player groups
vault-include-global: true
# Which storage method the plugin should use. # Which storage method the plugin should use.
# Currently supported: mysql, sqlite, h2, json, yaml, mongodb # Currently supported: mysql, sqlite, h2, json, yaml, mongodb
# Fill out connection info below if you're using MySQL or MongoDB # Fill out connection info below if you're using MySQL or MongoDB

View File

@ -1,13 +1,17 @@
name: LuckPerms name: LuckPerms
author: Luck
version: ${release.version}.${git.closest.tag.commit.count} version: ${release.version}.${git.closest.tag.commit.count}
description: A permissions plugin
author: Luck
website: https://github.com/lucko/LuckPerms
main: me.lucko.luckperms.LPBukkitPlugin main: me.lucko.luckperms.LPBukkitPlugin
softdepend: [Vault, PermissionsEx, GroupManager, PowerfulPerms, zPermissions, bPermissions] # For migration softdepend: [Vault, PermissionsEx, GroupManager, PowerfulPerms, zPermissions, bPermissions] # For migration
description: A permissions plugin
commands: commands:
luckperms: luckperms:
description: Manage permissions description: Manage permissions
aliases: [perms, permissions, lp, p, perm] aliases: [perms, permissions, lp, p, perm]
permissions: permissions:
luckperms.*: luckperms.*:
description: Gives access to all LuckPerms commands description: Gives access to all LuckPerms commands

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version> <version>2.7-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -33,7 +33,6 @@ import net.md_5.bungee.api.event.*;
import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventHandler;
import java.lang.ref.WeakReference;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -109,16 +108,10 @@ public class BungeeListener extends AbstractListener implements Listener {
@EventHandler @EventHandler
public void onPlayerPostLogin(PostLoginEvent e) { public void onPlayerPostLogin(PostLoginEvent e) {
final ProxiedPlayer player = e.getPlayer(); final ProxiedPlayer player = e.getPlayer();
final WeakReference<ProxiedPlayer> p = new WeakReference<>(player);
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId())); final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
if (user == null) { if (user == null) {
plugin.getProxy().getScheduler().schedule(plugin, () -> { plugin.getProxy().getScheduler().schedule(plugin, () -> player.sendMessage(WARN_MESSAGE), 3, TimeUnit.SECONDS);
final ProxiedPlayer pl = p.get();
if (pl != null) {
pl.sendMessage(WARN_MESSAGE);
}
}, 3, TimeUnit.SECONDS);
} else { } else {
user.refreshPermissions(); user.refreshPermissions();
} }

View File

@ -24,6 +24,7 @@ package me.lucko.luckperms;
import lombok.Getter; import lombok.Getter;
import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.commands.CommandManager; import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.ConsecutiveExecutor; import me.lucko.luckperms.commands.ConsecutiveExecutor;
@ -135,8 +136,8 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
} }
@Override @Override
public Type getType() { public PlatformType getType() {
return Type.BUNGEE; return PlatformType.BUNGEE;
} }
@Override @Override

View File

@ -1,6 +1,6 @@
name: LuckPerms name: LuckPerms
author: Luck
version: ${release.version}.${git.closest.tag.commit.count} version: ${release.version}.${git.closest.tag.commit.count}
main: me.lucko.luckperms.LPBungeePlugin
softdepend: [PowerfulPerms] # For migration
description: A permissions plugin description: A permissions plugin
author: Luck
main: me.lucko.luckperms.LPBungeePlugin
softdepends: [PowerfulPerms] # For migration

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version> <version>2.7-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -23,6 +23,7 @@
package me.lucko.luckperms; package me.lucko.luckperms;
import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.commands.ConsecutiveExecutor; import me.lucko.luckperms.commands.ConsecutiveExecutor;
import me.lucko.luckperms.commands.Sender; import me.lucko.luckperms.commands.Sender;
@ -68,7 +69,7 @@ public interface LuckPermsPlugin {
/** /**
* @return the platform type * @return the platform type
*/ */
Type getType(); PlatformType getType();
/** /**
* @return the main plugin directory * @return the main plugin directory
@ -166,8 +167,4 @@ public interface LuckPermsPlugin {
*/ */
void doSync(Runnable r); void doSync(Runnable r);
enum Type {
BUKKIT, BUNGEE, SPONGE;
}
} }

View File

@ -67,7 +67,7 @@ public class ApiProvider implements LuckPermsApi {
@Override @Override
public double getApiVersion() { public double getApiVersion() {
return 1.6; return 2.6;
} }
@Override @Override
@ -75,6 +75,11 @@ public class ApiProvider implements LuckPermsApi {
return plugin.getVersion(); return plugin.getVersion();
} }
@Override
public PlatformType getPlatformType() {
return plugin.getType();
}
@Override @Override
public void registerListener(@NonNull LPListener listener) { public void registerListener(@NonNull LPListener listener) {
eventBus.register(listener); eventBus.register(listener);

View File

@ -27,6 +27,8 @@ import me.lucko.luckperms.api.LPConfiguration;
import me.lucko.luckperms.api.data.DatastoreConfiguration; import me.lucko.luckperms.api.data.DatastoreConfiguration;
import me.lucko.luckperms.api.data.MySQLConfiguration; import me.lucko.luckperms.api.data.MySQLConfiguration;
import java.util.Map;
/** /**
* Provides a link between {@link LPConfiguration} and {@link me.lucko.luckperms.core.LPConfiguration} * Provides a link between {@link LPConfiguration} and {@link me.lucko.luckperms.core.LPConfiguration}
*/ */
@ -79,6 +81,21 @@ public class LPConfigurationLink implements LPConfiguration {
return master.getApplyShorthand(); return master.getApplyShorthand();
} }
@Override
public boolean getLogNotify() {
return master.getLogNotify();
}
@Override
public String getVaultServer() {
return master.getVaultServer();
}
@Override
public boolean getVaultIncludeGlobal() {
return master.getVaultIncludeGlobal();
}
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public MySQLConfiguration getDatabaseValues() { public MySQLConfiguration getDatabaseValues() {
@ -94,4 +111,15 @@ public class LPConfigurationLink implements LPConfiguration {
public String getStorageMethod() { public String getStorageMethod() {
return master.getStorageMethod(); return master.getStorageMethod();
} }
@Override
public boolean getSplitStorage() {
return master.getSplitStorage();
}
@SuppressWarnings("unchecked")
@Override
public Map<String, String> getSplitStorageOptions() {
return master.getSplitStorageOptions();
}
} }

View File

@ -110,10 +110,11 @@ public abstract class SubCommand<T> {
return permission.isAuthorized(sender); 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) { protected static List<String> getGroupTabComplete(List<String> args, LuckPermsPlugin plugin) {

View File

@ -108,7 +108,7 @@ public class LogExport extends SubCommand<Log> {
break track; 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()); data.add(b.toString());

View File

@ -24,6 +24,7 @@ package me.lucko.luckperms.commands.migration.subcommands;
import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.commands.CommandResult; import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate; import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender; import me.lucko.luckperms.commands.Sender;
@ -62,7 +63,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }
if (plugin.getType() != LuckPermsPlugin.Type.BUKKIT) { if (plugin.getType() != PlatformType.BUKKIT) {
// Sponge uses a completely different version of PEX. // Sponge uses a completely different version of PEX.
log.severe("PEX import is not supported on this platform."); log.severe("PEX import is not supported on this platform.");
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;

View File

@ -70,17 +70,19 @@ public abstract class LPConfiguration<T extends LuckPermsPlugin> {
} }
/** /**
* As of 1.6, this value is a constant * As of 2.6, this value is a constant
* @return the default group node * @return the default group node
*/ */
@SuppressWarnings("SameReturnValue")
public String getDefaultGroupNode() { public String getDefaultGroupNode() {
return "group.default"; return "group.default";
} }
/** /**
* As of 1.6, this value is a constant * As of 2.6, this value is a constant
* @return the name of the default group * @return the name of the default group
*/ */
@SuppressWarnings("SameReturnValue")
public String getDefaultGroupName() { public String getDefaultGroupName() {
return "default"; return "default";
} }
@ -109,6 +111,14 @@ public abstract class LPConfiguration<T extends LuckPermsPlugin> {
return getBoolean("log-notify", true); return getBoolean("log-notify", true);
} }
public String getVaultServer() {
return getString("vault-server", "global");
}
public boolean getVaultIncludeGlobal() {
return getBoolean("vault-include-global", true);
}
public DatastoreConfiguration getDatabaseValues() { public DatastoreConfiguration getDatabaseValues() {
return new DatastoreConfiguration( return new DatastoreConfiguration(
getString("data.address", null), getString("data.address", null),

View File

@ -30,7 +30,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
* @see me.lucko.luckperms.api.UuidCache * @see me.lucko.luckperms.api.UuidCache for docs
*/ */
public class UuidCache { public class UuidCache {

View File

@ -59,7 +59,6 @@ public class Importer {
running = true; running = true;
return true; return true;
} }
public void start(Sender executor, List<String> commands) { public void start(Sender executor, List<String> commands) {

View File

@ -53,6 +53,6 @@ public class UpdateTask implements Runnable {
// Refresh all online users. // Refresh all online users.
plugin.getUserManager().updateAllUsers(); plugin.getUserManager().updateAllUsers();
plugin.getApiProvider().fireEvent(new PostSyncEvent());; plugin.getApiProvider().fireEvent(new PostSyncEvent());
} }
} }

View File

@ -445,14 +445,12 @@ public class MongoDBDatastore extends Datastore {
The following two methods convert the node maps so they can be stored. */ The following two methods convert the node maps so they can be stored. */
private static <V> Map<String, V> convert(Map<String, V> map) { private static <V> Map<String, V> convert(Map<String, V> map) {
return map.entrySet().stream() return map.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey().replace(".", "[**DOT**]").replace("$", "[**DOLLAR**]"), e.getValue())) .collect(Collectors.toMap(e -> e.getKey().replace(".", "[**DOT**]").replace("$", "[**DOLLAR**]"), Map.Entry::getValue));
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
} }
private static <V> Map<String, V> revert(Map<String, V> map) { private static <V> Map<String, V> revert(Map<String, V> map) {
return map.entrySet().stream() return map.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey().replace("[**DOT**]", ".").replace("[**DOLLAR**]", "$"), e.getValue())) .collect(Collectors.toMap(e -> e.getKey().replace("[**DOT**]", ".").replace("[**DOLLAR**]", "$"), Map.Entry::getValue));
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
} }
private static Document fromUser(User user) { private static Document fromUser(User user) {

View File

@ -367,6 +367,6 @@ public class YAMLDatastore extends FlatfileDatastore {
} }
interface ReadOperation { interface ReadOperation {
boolean onRun(Map<String, Object> values) throws IOException; boolean onRun(Map<String, Object> values);
} }
} }

View File

@ -63,7 +63,7 @@ public abstract class AbstractManager<I, T extends Identifiable<I>> {
} }
} }
public void preSet(T t) { protected void preSet(T t) {
} }
@ -108,7 +108,7 @@ public abstract class AbstractManager<I, T extends Identifiable<I>> {
} }
} }
public void preUnload(T t) { protected void preUnload(T t) {
} }

View File

@ -6,7 +6,7 @@
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<version>2.6-SNAPSHOT</version> <version>2.7-SNAPSHOT</version>
<modules> <modules>
<module>common</module> <module>common</module>
<module>api</module> <module>api</module>
@ -37,7 +37,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<release.version>2.6</release.version> <release.version>2.7</release.version>
</properties> </properties>
<distributionManagement> <distributionManagement>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version> <version>2.7-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -25,6 +25,7 @@ package me.lucko.luckperms;
import com.google.inject.Inject; import com.google.inject.Inject;
import lombok.Getter; import lombok.Getter;
import me.lucko.luckperms.api.LuckPermsApi; import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.api.sponge.LuckPermsService; import me.lucko.luckperms.api.sponge.LuckPermsService;
import me.lucko.luckperms.commands.ConsecutiveExecutor; import me.lucko.luckperms.commands.ConsecutiveExecutor;
@ -196,8 +197,8 @@ public class LPSpongePlugin implements LuckPermsPlugin {
} }
@Override @Override
public Type getType() { public PlatformType getType() {
return Type.SPONGE; return PlatformType.SPONGE;
} }
@Override @Override

View File

@ -342,9 +342,7 @@ public class LuckPermsSubject implements Subject {
if (subject instanceof LuckPermsSubject) { if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject); LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream() Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
.map(context -> new AbstractMap.SimpleEntry<>(context.getKey(), context.getValue()))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
try { try {
holder.setPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier()) holder.setPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier())
@ -363,9 +361,7 @@ public class LuckPermsSubject implements Subject {
if (subject instanceof LuckPermsSubject) { if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject); LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream() Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
.map(context -> new AbstractMap.SimpleEntry<>(context.getKey(), context.getValue()))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
try { try {
holder.unsetPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier()) holder.unsetPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier())
@ -671,9 +667,7 @@ public class LuckPermsSubject implements Subject {
if (subject instanceof LuckPermsSubject) { if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject); LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream() Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
.map(context -> new AbstractMap.SimpleEntry<>(context.getKey(), context.getValue()))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
try { try {
holder.setTransientPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier()) holder.setTransientPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier())
@ -691,9 +685,7 @@ public class LuckPermsSubject implements Subject {
if (subject instanceof LuckPermsSubject) { if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject); LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream() Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
.map(context -> new AbstractMap.SimpleEntry<>(context.getKey(), context.getValue()))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
try { try {
holder.unsetTransientPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier()) holder.unsetTransientPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier())