Complete refactor of permissions/nodes. progress towards 1.6

This commit is contained in:
Luck
2016-08-27 00:32:39 +01:00
Unverified
parent c4497db06c
commit edaf174ebf
33 changed files with 1477 additions and 800 deletions
@@ -27,6 +27,7 @@ import lombok.AllArgsConstructor;
import lombok.NonNull;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.*;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.event.LPEvent;
import me.lucko.luckperms.api.event.LPListener;
import me.lucko.luckperms.api.implementation.internal.*;
@@ -36,6 +37,8 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static me.lucko.luckperms.api.implementation.internal.Utils.checkNode;
/**
* Provides static access to LuckPerms
*/
@@ -187,4 +190,9 @@ public class ApiProvider implements LuckPermsApi {
public boolean isTrackLoaded(@NonNull String name) {
return plugin.getTrackManager().isLoaded(name);
}
@Override
public Node.Builder buildNode(String permission) throws IllegalArgumentException {
return new me.lucko.luckperms.utils.Node.Builder(checkNode(permission));
}
}
@@ -24,15 +24,15 @@ package me.lucko.luckperms.api.implementation.internal;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import static me.lucko.luckperms.api.implementation.internal.Utils.*;
import static me.lucko.luckperms.core.PermissionHolder.convertToLegacy;
/**
* Provides a link between {@link PermissionHolder} and {@link me.lucko.luckperms.core.PermissionHolder}
@@ -49,9 +49,24 @@ public class PermissionHolderLink implements PermissionHolder {
return master.getObjectName();
}
@Override
public Set<Node> getPermissionNodes() {
return Collections.unmodifiableSet(master.getNodes());
}
@Override
public Set<Node> getAllNodes() {
return Collections.unmodifiableSet(master.getAllNodes(null));
}
@Override
public Map<String, Boolean> getNodes() {
return Collections.unmodifiableMap(master.getNodes());
return convertToLegacy(master.getNodes());
}
@Override
public boolean hasPermission(@NonNull Node node) {
return master.hasPermission(node);
}
@Override
@@ -84,6 +99,11 @@ public class PermissionHolderLink implements PermissionHolder {
return master.hasPermission(node, b, checkServer(server), world, temporary);
}
@Override
public boolean inheritsPermission(@NonNull Node node) {
return master.inheritsPermission(node);
}
@Override
public boolean inheritsPermission(@NonNull String node, @NonNull boolean b) {
return master.inheritsPermission(node, b);
@@ -114,6 +134,11 @@ public class PermissionHolderLink implements PermissionHolder {
return master.inheritsPermission(node, b, checkServer(server), world, temporary);
}
@Override
public void setPermission(@NonNull Node node) throws ObjectAlreadyHasException {
master.setPermission(node);
}
@Override
public void setPermission(@NonNull String node, @NonNull boolean value) throws ObjectAlreadyHasException {
master.setPermission(checkNode(node), value);
@@ -144,6 +169,11 @@ public class PermissionHolderLink implements PermissionHolder {
master.setPermission(checkNode(node), value, checkServer(server), world, checkTime(expireAt));
}
@Override
public void unsetPermission(@NonNull Node node) throws ObjectLacksException {
master.unsetPermission(node);
}
@Override
public void unsetPermission(@NonNull String node, @NonNull boolean temporary) throws ObjectLacksException {
master.unsetPermission(checkNode(node), temporary);
@@ -194,13 +224,34 @@ public class PermissionHolderLink implements PermissionHolder {
return master.getLocalPermissions(server, excludedGroups);
}
@Override
public Map<String, Boolean> getPermissions(String server, String world, Map<String, String> extraContext, boolean includeGlobal, List<String> possibleNodes, boolean applyGroups) {
return master.getPermissions(server, world, extraContext, includeGlobal, possibleNodes, applyGroups);
}
@Override
public Map<Map.Entry<String, Boolean>, Long> getTemporaryNodes() {
Map<Map.Entry<String, Boolean>, Long> m = new HashMap<>();
for (Node node : master.getTemporaryNodes()) {
m.put(new AbstractMap.SimpleEntry<>(node.getKey(), node.getValue()), node.getExpiryUnixTime());
}
return m;
}
@Override
public Set<Node> getTemporaryPermissionNodes() {
return master.getTemporaryNodes();
}
@Override
public Map<String, Boolean> getPermanentNodes() {
return convertToLegacy(master.getPermanentNodes());
}
@Override
public Set<Node> getPermanentPermissionNodes() {
return master.getPermanentNodes();
}
@@ -29,27 +29,27 @@ import me.lucko.luckperms.api.User;
import me.lucko.luckperms.utils.ArgumentChecker;
@UtilityClass
class Utils {
public class Utils {
static void checkUser(User user) {
public static void checkUser(User user) {
if (!(user instanceof UserLink)) {
throw new IllegalStateException("User instance cannot be handled by this implementation.");
}
}
static void checkGroup(Group group) {
public static void checkGroup(Group group) {
if (!(group instanceof GroupLink)) {
throw new IllegalStateException("Group instance cannot be handled by this implementation.");
}
}
static void checkTrack(Track track) {
public static void checkTrack(Track track) {
if (!(track instanceof TrackLink)) {
throw new IllegalStateException("Track instance cannot be handled by this implementation.");
}
}
static String checkUsername(String s) {
public static String checkUsername(String s) {
if (ArgumentChecker.checkUsername(s)) {
throw new IllegalArgumentException("Invalid username entry '" + s + "'. Usernames must be less than 16 chars" +
" and only contain 'a-z A-Z 1-9 _'.");
@@ -57,7 +57,7 @@ class Utils {
return s;
}
static String checkName(String s) {
public static String checkName(String s) {
if (ArgumentChecker.checkName(s)) {
throw new IllegalArgumentException("Invalid name entry '" + s + "'. Names must be less than 37 chars" +
" and only contain 'a-z A-Z 1-9'.");
@@ -65,21 +65,21 @@ class Utils {
return s.toLowerCase();
}
static String checkServer(String s) {
public static String checkServer(String s) {
if (ArgumentChecker.checkServer(s)) {
throw new IllegalArgumentException("Invalid server entry '" + s + "'. Server names can only contain alphanumeric characters.");
}
return s;
}
static String checkNode(String s) {
public static String checkNode(String s) {
if (ArgumentChecker.checkNode(s)) {
throw new IllegalArgumentException("Invalid node entry '" + s + "'. Nodes cannot contain '/' or '$' characters.");
}
return s;
}
static long checkTime(long l) {
public static long checkTime(long l) {
if (ArgumentChecker.checkTime(l)) {
throw new IllegalArgumentException("Unix time '" + l + "' is invalid, as it has already passed.");
}
@@ -43,8 +43,8 @@ public class GroupInfo extends SubCommand<Group> {
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
Message.GROUP_INFO.send(sender,
group.getName(),
group.getPermanentNodes().keySet().size(),
group.getTemporaryNodes().keySet().size(),
group.getPermanentNodes().size(),
group.getTemporaryNodes().size(),
label,
group.getName()
);
@@ -30,6 +30,8 @@ import me.lucko.luckperms.groups.Group;
import java.util.List;
import static me.lucko.luckperms.core.PermissionHolder.convertToLegacy;
public class GroupListNodes extends SubCommand<Group> {
public GroupListNodes() {
super("listnodes", "Lists the permission nodes the group has", "/%s group <group> listnodes",
@@ -38,8 +40,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(group.getPermanentNodes()));
Message.LISTNODES_TEMP.send(sender, group.getName(), Util.tempNodesToString(group.getTemporaryNodes()));
Message.LISTNODES.send(sender, group.getName(), Util.permNodesToString(convertToLegacy(group.getPermanentNodes())));
Message.LISTNODES_TEMP.send(sender, group.getName(), Util.tempNodesToString(group.getTemporaryNodesLegacy()));
return CommandResult.SUCCESS;
}
}
@@ -43,8 +43,8 @@ public class UserInfo extends SubCommand<User> {
plugin.getPlayerStatus(user.getUuid()),
Util.listToCommaSep(user.getGroupNames()),
user.getPrimaryGroup(),
user.getPermanentNodes().keySet().size(),
user.getTemporaryNodes().keySet().size(),
user.getPermanentNodes().size(),
user.getTemporaryNodes().size(),
label,
user.getName()
);
@@ -26,10 +26,13 @@ import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.users.User;
import java.util.List;
import static me.lucko.luckperms.core.PermissionHolder.*;
public class UserListNodes extends SubCommand<User> {
public UserListNodes() {
super("listnodes", "Lists the permission nodes the user has", "/%s user <user> listnodes",
@@ -38,8 +41,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(user.getPermanentNodes()));
Message.LISTNODES_TEMP.send(sender, user.getName(), Util.tempNodesToString(user.getTemporaryNodes()));
Message.LISTNODES.send(sender, user.getName(), Util.permNodesToString(convertToLegacy(user.getPermanentNodes())));
Message.LISTNODES_TEMP.send(sender, user.getName(), Util.tempNodesToString(user.getTemporaryNodesLegacy()));
return CommandResult.SUCCESS;
}
}
File diff suppressed because it is too large Load Diff
@@ -321,7 +321,7 @@ public class Group extends PermissionHolder implements Identifiable<String> {
*/
private List<String> getGroups(String server, String world, boolean includeGlobal) {
// Call super #getPermissions method, and just sort through those
Map<String, Boolean> perms = getPermissions(server, world, null, includeGlobal, null);
Map<String, Boolean> perms = getPermissions(server, world, null, includeGlobal, null, true);
return perms.keySet().stream()
.filter(s -> Patterns.GROUP_MATCH.matcher(s).matches())
.map(s -> Patterns.DOT.split(s, 2)[1])
@@ -28,11 +28,13 @@ import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.data.Log;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.Node;
import java.io.*;
import java.util.*;
@@ -41,6 +43,8 @@ import java.util.logging.*;
import java.util.logging.Formatter;
import java.util.stream.Collectors;
import static me.lucko.luckperms.core.PermissionHolder.*;
@SuppressWarnings({"ResultOfMethodCallIgnored", "UnnecessaryLocalVariable"})
public class FlatfileDatastore extends Datastore {
private static final String LOG_FORMAT = "%s(%s): [%s] %s(%s) --> %s";
@@ -184,7 +188,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : user.getNodes().entrySet()) {
for (Map.Entry<String, Boolean> e : convertToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
@@ -209,7 +213,7 @@ public class FlatfileDatastore extends Datastore {
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
user.getNodes().put(node, b);
user.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
@@ -225,7 +229,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : user.getNodes().entrySet()) {
for (Map.Entry<String, Boolean> e : convertToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
@@ -260,7 +264,7 @@ public class FlatfileDatastore extends Datastore {
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
user.getNodes().put(node, b);
user.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
@@ -291,7 +295,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : user.getNodes().entrySet()) {
for (Map.Entry<String, Boolean> e : convertToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
@@ -319,7 +323,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : group.getNodes().entrySet()) {
for (Map.Entry<String, Boolean> e : convertToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
@@ -339,7 +343,7 @@ public class FlatfileDatastore extends Datastore {
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
group.getNodes().put(node, b);
group.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
@@ -369,7 +373,7 @@ public class FlatfileDatastore extends Datastore {
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
group.getNodes().put(node, b);
group.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
@@ -411,7 +415,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : group.getNodes().entrySet()) {
for (Map.Entry<String, Boolean> e : convertToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
@@ -31,6 +31,7 @@ import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.InsertOneOptions;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.data.Log;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.groups.GroupManager;
@@ -45,6 +46,8 @@ import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import static me.lucko.luckperms.core.PermissionHolder.convertToLegacy;
@SuppressWarnings("unchecked")
public class MongoDBDatastore extends Datastore {
@@ -427,7 +430,7 @@ public class MongoDBDatastore extends Datastore {
.append("primaryGroup", user.getPrimaryGroup());
Document perms = new Document();
for (Map.Entry<String, Boolean> e : convert(user.getNodes()).entrySet()) {
for (Map.Entry<String, Boolean> e : convert(convertToLegacy(user.getNodes())).entrySet()) {
perms.append(e.getKey(), e.getValue());
}
@@ -439,7 +442,7 @@ public class MongoDBDatastore extends Datastore {
Document main = new Document("_id", group.getName());
Document perms = new Document();
for (Map.Entry<String, Boolean> e : convert(group.getNodes()).entrySet()) {
for (Map.Entry<String, Boolean> e : convert(convertToLegacy(group.getNodes())).entrySet()) {
perms.append(e.getKey(), e.getValue());
}
@@ -109,7 +109,8 @@ abstract class SQLDatastore extends Datastore {
boolean onResult(ResultSet resultSet) throws SQLException {
if (resultSet.next()) {
user.setName(resultSet.getString("name"));
user.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
user.setNodes(nodes);
user.setPrimaryGroup(resultSet.getString("primary_group"));
return true;
}
@@ -193,7 +194,8 @@ abstract class SQLDatastore extends Datastore {
}
});
} else {
user.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
user.setNodes(nodes);
user.setPrimaryGroup(resultSet.getString("primary_group"));
if (!resultSet.getString("name").equals(user.getName())) {
@@ -251,7 +253,8 @@ abstract class SQLDatastore extends Datastore {
}
});
} else {
group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
group.setNodes(nodes);
}
return success;
}
@@ -273,7 +276,8 @@ abstract class SQLDatastore extends Datastore {
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
if (resultSet.next()) {
group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
group.setNodes(nodes);
return true;
}
return false;
@@ -297,7 +301,8 @@ abstract class SQLDatastore extends Datastore {
boolean onResult(ResultSet resultSet) throws SQLException {
while (resultSet.next()) {
Group group = plugin.getGroupManager().make(resultSet.getString("name"));
group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
group.setNodes(nodes);
groups.add(group);
}
return true;
@@ -288,7 +288,9 @@ public abstract class User extends PermissionHolder implements Identifiable<UUID
public void clearNodes() {
String defaultGroupNode = getPlugin().getConfiguration().getDefaultGroupNode();
getNodes().clear();
getNodes().put(defaultGroupNode, true);
try {
setPermission(defaultGroupNode, true);
} catch (ObjectAlreadyHasException ignored) {}
}
/**
@@ -328,7 +330,7 @@ public abstract class User extends PermissionHolder implements Identifiable<UUID
*/
private List<String> getGroups(String server, String world, boolean includeGlobal) {
// Call super #getPermissions method, and just sort through those
Map<String, Boolean> perms = getPermissions(server, world, null, includeGlobal, null);
Map<String, Boolean> perms = getPermissions(server, world, null, includeGlobal, null, true);
return perms.keySet().stream()
.filter(s -> Patterns.GROUP_MATCH.matcher(s).matches())
.map(s -> Patterns.DOT.split(s, 2)[1])
@@ -0,0 +1,530 @@
/*
* 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.utils;
import com.google.common.collect.ImmutableMap;
import lombok.*;
import me.lucko.luckperms.constants.Patterns;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* An immutable permission node
*/
@SuppressWarnings({"WeakerAccess", "unused"})
@ToString
@EqualsAndHashCode
public class Node implements me.lucko.luckperms.api.Node {
public static me.lucko.luckperms.api.Node fromSerialisedNode(String s, Boolean b) {
return builderFromSerialisedNode(s, b).build();
}
public static me.lucko.luckperms.api.Node.Builder builderFromSerialisedNode(String s, Boolean b) {
if (s.contains("/")) {
String[] parts = Patterns.SERVER_DELIMITER.split(s, 2);
// 0=server(+world) 1=node
// WORLD SPECIFIC
if (parts[0].contains("-")) {
String[] serverParts = Patterns.WORLD_DELIMITER.split(parts[0], 2);
// 0=server 1=world
if (parts[1].contains("$")) {
String[] tempParts = Patterns.TEMP_DELIMITER.split(parts[1], 2);
return new Node.Builder(tempParts[0], true).setServerRaw(serverParts[0]).setWorld(serverParts[1])
.setExpiry(Long.parseLong(tempParts[1])).setValue(b);
} else {
return new Node.Builder(parts[1], true).setServerRaw(serverParts[0]).setWorld(serverParts[1]).setValue(b);
}
} else {
// SERVER BUT NOT WORLD SPECIFIC
if (parts[1].contains("$")) {
String[] tempParts = Patterns.TEMP_DELIMITER.split(parts[1], 2);
return new Node.Builder(tempParts[0], true).setServerRaw(parts[0]).setExpiry(Long.parseLong(tempParts[1])).setValue(b);
} else {
return new Node.Builder(parts[1], true).setServerRaw(parts[0]).setValue(b);
}
}
} else {
// NOT SERVER SPECIFIC
if (s.contains("$")) {
String[] tempParts = Patterns.TEMP_DELIMITER.split(s, 2);
return new Node.Builder(tempParts[0], true).setExpiry(Long.parseLong(tempParts[1])).setValue(b);
} else {
return new Node.Builder(s, true).setValue(b);
}
}
}
@Getter
private final String permission;
@Getter
private Boolean value;
private String server = null;
private String world = null;
private long expireAt = 0L;
private final Map<String, String> extraContexts = new HashMap<>();
/**
* Make an immutable node instance
* @param permission the actual permission node
* @param value the value (if it's *not* negated)
* @param expireAt the time when the node will expire
* @param server the server this node applies on
* @param world the world this node applies on
* @param extraContexts any additional contexts applying to this node
*/
public Node(String permission, boolean value, long expireAt, String server, String world, Map<String, String> extraContexts) {
if (permission == null || permission.equals("")) {
throw new IllegalArgumentException("Empty permission");
}
if (server != null && (server.equalsIgnoreCase("global") || server.equals(""))) {
server = null;
}
if (world != null && world.equals("")) {
world = null;
}
if (world != null && server == null) {
server = "global";
}
this.permission = permission;
this.value = value;
this.expireAt = expireAt;
this.server = server;
this.world = world;
if (extraContexts != null) {
this.extraContexts.putAll(extraContexts);
}
}
public boolean isNegated() {
return !value;
}
public Optional<String> getServer() {
return Optional.ofNullable(server);
}
public Optional<String> getWorld() {
return Optional.ofNullable(world);
}
public boolean isServerSpecific() {
return getServer().isPresent();
}
public boolean isWorldSpecific() {
return getWorld().isPresent();
}
@Override
public boolean shouldApplyOnServer(String server, boolean includeGlobal, boolean applyRegex) {
if (server == null || server.equals("")) {
return true;
}
if (isServerSpecific()) {
if (server.toLowerCase().startsWith("r=") && applyRegex) {
Pattern p = Patterns.compile(server.substring(2));
if (p == null) {
return false;
}
return p.matcher(this.server).matches();
}
if (server.startsWith("(") && server.endsWith(")") && server.contains("|")) {
final String bits = server.substring(1, server.length() - 1);
String[] parts = Patterns.VERTICAL_BAR.split(bits);
for (String s : parts) {
if (s.equalsIgnoreCase(this.server)) {
return true;
}
}
return false;
}
return this.server.equalsIgnoreCase(server);
} else {
return includeGlobal;
}
}
@Override
public boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex) {
if (world == null || world.equals("")) {
return true;
}
if (isWorldSpecific()) {
if (world.toLowerCase().startsWith("r=") && applyRegex) {
Pattern p = Patterns.compile(world.substring(2));
if (p == null) {
return false;
}
return p.matcher(this.world).matches();
}
if (world.startsWith("(") && world.endsWith(")") && world.contains("|")) {
final String bits = world.substring(1, world.length() - 1);
String[] parts = Patterns.VERTICAL_BAR.split(bits);
for (String s : parts) {
if (s.equalsIgnoreCase(this.world)) {
return true;
}
}
return false;
}
return this.world.equalsIgnoreCase(world);
} else {
return includeGlobal;
}
}
@Override
public boolean shouldApplyWithContext(Map<String, String> context) {
if (context == null || context.isEmpty()) {
return true;
}
for (Map.Entry<String, String> c : context.entrySet()) {
if (!getExtraContexts().containsKey(c.getKey())) {
return false;
}
if (!getExtraContexts().get(c.getKey()).equalsIgnoreCase(c.getValue())) {
return false;
}
}
return true;
}
@Override
public boolean shouldApplyOnAnyServers(List<String> servers, boolean includeGlobal) {
for (String s : servers) {
if (shouldApplyOnServer(s, includeGlobal, false)) {
return true;
}
}
return false;
}
@Override
public boolean shouldApplyOnAnyWorlds(List<String> worlds, boolean includeGlobal) {
for (String s : worlds) {
if (shouldApplyOnWorld(s, includeGlobal, false)) {
return true;
}
}
return false;
}
@Override
public List<String> resolveWildcard(List<String> possibleNodes) {
if (!isWildcard() || possibleNodes == null) {
return Collections.emptyList();
}
String match = getPermission().substring(0, getPermission().length() - 2);
return possibleNodes.stream().filter(pn -> pn.startsWith(match)).collect(Collectors.toList());
}
@Override
public List<String> resolveShorthand() {
if (!Patterns.SHORTHAND_NODE.matcher(getPermission()).find()) {
return Collections.emptyList();
}
if (!getPermission().contains(".")) {
return Collections.emptyList();
}
String[] parts = Patterns.DOT.split(getPermission());
List<Set<String>> nodeParts = new ArrayList<>();
for (String s : parts) {
if ((!s.startsWith("(") || !s.endsWith(")")) || !s.contains("|")) {
nodeParts.add(Collections.singleton(s));
continue;
}
final String bits = s.substring(1, s.length() - 1);
nodeParts.add(new HashSet<>(Arrays.asList(Patterns.VERTICAL_BAR.split(bits))));
}
Set<String> nodes = new HashSet<>();
for (Set<String> set : nodeParts) {
final Set<String> newNodes = new HashSet<>();
if (nodes.isEmpty()) {
newNodes.addAll(set);
} else {
nodes.forEach(str -> newNodes.addAll(set.stream()
.map(add -> str + "." + add)
.collect(Collectors.toList()))
);
}
nodes = newNodes;
}
return new ArrayList<>(nodes);
}
public boolean isTemporary() {
return expireAt != 0L;
}
public boolean isPermanent() {
return !isTemporary();
}
public long getExpiryUnixTime(){
return expireAt;
}
public Date getExpiry() {
return new Date(expireAt * 1000L);
}
public long getSecondsTilExpiry() {
return expireAt - (System.currentTimeMillis() / 1000L);
}
public boolean hasExpired() {
return expireAt < (System.currentTimeMillis() / 1000L);
}
public Map<String, String> getExtraContexts() {
return ImmutableMap.copyOf(extraContexts);
}
public String toSerializedNode() {
StringBuilder builder = new StringBuilder();
if (server != null) {
builder.append(server);
if (world != null) {
builder.append("-").append(world);
}
builder.append("/");
} else {
if (world != null) {
builder.append("global-").append(world).append("/");
}
}
if (!extraContexts.isEmpty()) {
builder.append("(");
for (Map.Entry<String, String> entry : extraContexts.entrySet()) {
builder.append(entry.getKey()).append("=").append(entry.getValue()).append(",");
}
builder.deleteCharAt(builder.length() - 1);
builder.append(")");
}
builder.append(permission);
if (expireAt != 0L) {
builder.append("$").append(expireAt);
}
return builder.toString();
}
@Override
public boolean isGroupNode() {
return Patterns.GROUP_MATCH.matcher(getPermission()).matches();
}
@Override
public String getGroupName() {
if (!isGroupNode()) {
throw new IllegalStateException("This is not a group node");
}
return getPermission().substring("group.".length());
}
@Override
public boolean isWildcard() {
return getPermission().endsWith(".*");
}
@Override
public int getWildcardLevel() {
return (int) getPermission().chars().filter(num -> num == Character.getNumericValue('.')).count();
}
@Override
public boolean almostEquals(me.lucko.luckperms.api.Node other) {
if (!other.getPermission().equalsIgnoreCase(this.getPermission())) {
return false;
}
if (other.getServer().isPresent() != this.getServer().isPresent()) {
if (other.getServer().isPresent()) {
if (!other.getServer().get().equalsIgnoreCase(this.getServer().get())) {
return false;
}
}
} else {
return false;
}
if (other.getWorld().isPresent() != this.getWorld().isPresent()) {
if (other.getWorld().isPresent()) {
if (!other.getWorld().get().equalsIgnoreCase(this.getWorld().get())) {
return false;
}
}
} else {
return false;
}
if (!other.getExtraContexts().equals(this.getExtraContexts())) {
return false;
}
if (other.isTemporary() != this.isTemporary()) {
return false;
}
return true;
}
@Override
public Boolean setValue(Boolean value) {
throw new UnsupportedOperationException();
}
@Override
public String getKey() {
return getPermission();
}
@RequiredArgsConstructor
public static class Builder implements me.lucko.luckperms.api.Node.Builder {
private final String permission;
private Boolean value = true;
private String server = null;
private String world = null;
private long expireAt = 0L;
private final Map<String, String> extraContexts = new HashMap<>();
Builder(String permission, boolean shouldConvertContexts) {
if (!shouldConvertContexts) {
this.permission = permission;
} else {
if (!Patterns.NODE_CONTEXTS.matcher(permission).matches()) {
this.permission = permission;
} else {
String[] contextParts = permission.substring(1).split("\\)", 2);
// 0 = context, 1 = node
this.permission = contextParts[1];
for (String s : contextParts[0].split("\\,")) {
if (!s.contains("=")) {
// Not valid
continue;
}
String[] context = s.split("\\=", 2);
extraContexts.put(context[0], context[1]);
}
}
}
}
@Override
public me.lucko.luckperms.api.Node.Builder setNegated(boolean negated) {
value = !negated;
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder setValue(boolean value) {
this.value = value;
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder setExpiry(long expireAt) {
this.expireAt = expireAt;
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder setWorld(String world) {
this.world = world;
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder setServer(String server) {
if (server != null && ArgumentChecker.checkServer(server)) {
throw new IllegalArgumentException("Server name invalid.");
}
this.server = server;
return this;
}
public me.lucko.luckperms.api.Node.Builder setServerRaw(String server) {
this.server = server;
return this;
}
@Override
public me.lucko.luckperms.api.Node.Builder withExtraContext(@NonNull String key, @NonNull String value) {
this.extraContexts.put(key, value);
return this;
}
@Override
public me.lucko.luckperms.api.Node build() {
return new Node(permission, value, expireAt, server, world, extraContexts);
}
}
}