Refactor PermissionHolder & Sponge subjects

This commit is contained in:
Luck 2016-10-02 15:05:53 +01:00
parent 18e126b781
commit 1b3b50c113
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
15 changed files with 875 additions and 904 deletions

View File

@ -29,13 +29,15 @@ import me.lucko.luckperms.api.vault.cache.VaultUserCache;
import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.contexts.Contexts;
import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group; 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.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters; import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters;
import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters; import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters;
@ -75,18 +77,22 @@ public class VaultChatHook extends Chat {
private void saveMeta(PermissionHolder holder, String world, String node, String value) { private void saveMeta(PermissionHolder holder, String world, String node, String value) {
if (holder == null) return; if (holder == null) return;
if (node.equals("")) return; if (node.equals("")) return;
node = escapeCharacters(node);
value = escapeCharacters(value);
Iterator<Node> nodes = holder.getNodes().iterator(); perms.scheduleTask(() -> {
while (nodes.hasNext()) { String k = escapeCharacters(node);
Node n = nodes.next(); String v = escapeCharacters(value);
if (n.isMeta() && n.getMeta().getKey().equals(node)) {
nodes.remove();
}
}
Node.Builder metaNode = new me.lucko.luckperms.core.Node.Builder("meta." + node + "." + value).setValue(true); List<Node> toRemove = holder.getNodes().stream()
.filter(n -> n.isMeta() && n.getMeta().getKey().equals(k))
.collect(Collectors.toList());
toRemove.forEach(n -> {
try {
holder.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
});
Node.Builder metaNode = new me.lucko.luckperms.core.Node.Builder("meta." + k + "." + v).setValue(true);
if (!perms.getServer().equalsIgnoreCase("global")) { if (!perms.getServer().equalsIgnoreCase("global")) {
metaNode.setServer(perms.getServer()); metaNode.setServer(perms.getServer());
} }
@ -98,13 +104,15 @@ public class VaultChatHook extends Chat {
holder.setPermission(metaNode.build()); holder.setPermission(metaNode.build());
} catch (ObjectAlreadyHasException ignored) {} } catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(holder); perms.save(holder);
});
} }
private void setChatMeta(boolean prefix, PermissionHolder holder, String value, String world) { private void setChatMeta(boolean prefix, PermissionHolder holder, String value, String world) {
if (holder == null) return; if (holder == null) return;
if (value.equals("")) return; if (value.equals("")) return;
perms.scheduleTask(() -> {
Node.Builder node = new me.lucko.luckperms.core.Node.Builder(prefix ? "prefix" : "suffix" + ".1000." + escapeCharacters(value)); Node.Builder node = new me.lucko.luckperms.core.Node.Builder(prefix ? "prefix" : "suffix" + ".1000." + escapeCharacters(value));
node.setValue(true); node.setValue(true);
if (!perms.getServer().equalsIgnoreCase("global")) { if (!perms.getServer().equalsIgnoreCase("global")) {
@ -119,7 +127,8 @@ public class VaultChatHook extends Chat {
holder.setPermission(node.build()); holder.setPermission(node.build());
} catch (ObjectAlreadyHasException ignored) {} } catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(holder); perms.save(holder);
});
} }
private String getUserMeta(User user, String world, String node, String defaultValue) { private String getUserMeta(User user, String world, String node, String defaultValue) {

View File

@ -27,7 +27,6 @@ import lombok.NonNull;
import lombok.Setter; import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin; import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.api.vault.cache.VaultUserCache; import me.lucko.luckperms.api.vault.cache.VaultUserCache;
import me.lucko.luckperms.api.vault.cache.VaultUserManager; import me.lucko.luckperms.api.vault.cache.VaultUserManager;
import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.contexts.Contexts;
@ -38,11 +37,10 @@ import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
import net.milkbowl.vault.permission.Permission; import net.milkbowl.vault.permission.Permission;
import java.util.Collections; import java.util.*;
import java.util.HashMap;
import java.util.Map;
public class VaultPermissionHook extends Permission { public class VaultPermissionHook extends Permission implements Runnable {
private final List<Runnable> tasks = new ArrayList<>();
@Getter @Getter
@Setter @Setter
@ -76,6 +74,7 @@ public class VaultPermissionHook extends Permission {
public void setup() { public void setup() {
vaultUserManager = new VaultUserManager(plugin, this); vaultUserManager = new VaultUserManager(plugin, this);
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this, 1L, 1L);
} }
public void log(String s) { public void log(String s) {
@ -84,6 +83,23 @@ public class VaultPermissionHook extends Permission {
} }
} }
void scheduleTask(Runnable r) {
synchronized (tasks) {
tasks.add(r);
}
}
@Override
public void run() {
List<Runnable> toRun = new ArrayList<>();
synchronized (tasks) {
toRun.addAll(tasks);
tasks.clear();
}
toRun.forEach(Runnable::run);
}
private boolean objectHas(String world, Group group, String permission) { private boolean objectHas(String world, Group group, String permission) {
if (group == null) return false; if (group == null) return false;
@ -101,7 +117,7 @@ public class VaultPermissionHook extends Permission {
return toApply.containsKey(permission) && toApply.get(permission); return toApply.containsKey(permission) && toApply.get(permission);
} }
private boolean objectAdd(String world, PermissionHolder object, String permission) { private boolean add(String world, PermissionHolder object, String permission) {
if (object == null) return false; if (object == null) return false;
try { try {
@ -112,11 +128,11 @@ public class VaultPermissionHook extends Permission {
} }
} catch (ObjectAlreadyHasException ignored) {} } catch (ObjectAlreadyHasException ignored) {}
objectSave(object); save(object);
return true; return true;
} }
private boolean objectRemove(String world, PermissionHolder object, String permission) { private boolean remove(String world, PermissionHolder object, String permission) {
if (object == null) return false; if (object == null) return false;
try { try {
@ -127,17 +143,18 @@ public class VaultPermissionHook extends Permission {
} }
} catch (ObjectLacksException ignored) {} } catch (ObjectLacksException ignored) {}
objectSave(object); save(object);
return true; return true;
} }
void objectSave(PermissionHolder t) { void save(PermissionHolder t) {
if (t instanceof User) { if (t instanceof User) {
((User) t).refreshPermissions(); ((User) t).refreshPermissions();
plugin.getDatastore().saveUser(((User) t), Callback.empty()); plugin.getDatastore().saveUser(((User) t));
} }
if (t instanceof Group) { if (t instanceof Group) {
plugin.getDatastore().saveGroup(((Group) t), c -> plugin.runUpdateTask()); plugin.getDatastore().saveGroup(((Group) t));
plugin.runUpdateTask();
} }
} }
@ -165,14 +182,16 @@ public class VaultPermissionHook extends Permission {
public boolean playerAdd(String world, @NonNull String player, @NonNull String permission) { public boolean playerAdd(String world, @NonNull String player, @NonNull String permission) {
log("Adding permission to player " + player + ": '" + permission + "' on world " + world + ", server " + server); log("Adding permission to player " + player + ": '" + permission + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().get(player); final User user = plugin.getUserManager().get(player);
return objectAdd(world, user, permission); scheduleTask(() -> add(world, user, permission));
return true;
} }
@Override @Override
public boolean playerRemove(String world, @NonNull String player, @NonNull String permission) { public boolean playerRemove(String world, @NonNull String player, @NonNull String permission) {
log("Removing permission from player " + player + ": '" + permission + "' on world " + world + ", server " + server); log("Removing permission from player " + player + ": '" + permission + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().get(player); final User user = plugin.getUserManager().get(player);
return objectRemove(world, user, permission); scheduleTask(() -> remove(world, user, permission));
return true;
} }
@Override @Override
@ -186,14 +205,16 @@ public class VaultPermissionHook extends Permission {
public boolean groupAdd(String world, @NonNull String groupName, @NonNull String permission) { public boolean groupAdd(String world, @NonNull String groupName, @NonNull String permission) {
log("Adding permission to group " + groupName + ": '" + permission + "' on world " + world + ", server " + server); log("Adding permission to group " + groupName + ": '" + permission + "' on world " + world + ", server " + server);
final Group group = plugin.getGroupManager().get(groupName); final Group group = plugin.getGroupManager().get(groupName);
return objectAdd(world, group, permission); scheduleTask(() -> add(world, group, permission));
return true;
} }
@Override @Override
public boolean groupRemove(String world, @NonNull String groupName, @NonNull String permission) { public boolean groupRemove(String world, @NonNull String groupName, @NonNull String permission) {
log("Removing permission from group " + groupName + ": '" + permission + "' on world " + world + ", server " + server); log("Removing permission from group " + groupName + ": '" + permission + "' on world " + world + ", server " + server);
final Group group = plugin.getGroupManager().get(groupName); final Group group = plugin.getGroupManager().get(groupName);
return objectRemove(world, group, permission); scheduleTask(() -> remove(world, group, permission));
return true;
} }
@Override @Override
@ -213,13 +234,14 @@ public class VaultPermissionHook extends Permission {
@Override @Override
public boolean playerAddGroup(String world, @NonNull String player, @NonNull String groupName) { public boolean playerAddGroup(String world, @NonNull String player, @NonNull String groupName) {
log("Adding player " + player + " to group: '" + groupName + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().get(player); final User user = plugin.getUserManager().get(player);
if (user == null) return false; if (user == null) return false;
final Group group = plugin.getGroupManager().get(groupName); final Group group = plugin.getGroupManager().get(groupName);
if (group == null) return false; if (group == null) return false;
scheduleTask(() -> {
log("Adding player " + player + " to group: '" + groupName + "' on world " + world + ", server " + server); // todo move
try { try {
if (world != null && !world.equals("")) { if (world != null && !world.equals("")) {
user.addGroup(group, server, world); user.addGroup(group, server, world);
@ -227,19 +249,22 @@ public class VaultPermissionHook extends Permission {
user.addGroup(group, server); user.addGroup(group, server);
} }
} catch (ObjectAlreadyHasException ignored) {} } catch (ObjectAlreadyHasException ignored) {}
objectSave(user); save(user);
});
return true; return true;
} }
@Override @Override
public boolean playerRemoveGroup(String world, @NonNull String player, @NonNull String groupName) { public boolean playerRemoveGroup(String world, @NonNull String player, @NonNull String groupName) {
log("Removing player " + player + " from group: '" + groupName + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().get(player); final User user = plugin.getUserManager().get(player);
if (user == null) return false; if (user == null) return false;
final Group group = plugin.getGroupManager().get(groupName); final Group group = plugin.getGroupManager().get(groupName);
if (group == null) return false; if (group == null) return false;
scheduleTask(() -> {
log("Removing player " + player + " from group: '" + groupName + "' on world " + world + ", server " + server); // todo move
plugin.getLog().info("before: " + user.getNodes().toString());
try { try {
if (world != null && !world.equals("")) { if (world != null && !world.equals("")) {
user.removeGroup(group, server, world); user.removeGroup(group, server, world);
@ -247,7 +272,9 @@ public class VaultPermissionHook extends Permission {
user.removeGroup(group, server); user.removeGroup(group, server);
} }
} catch (ObjectLacksException ignored) {} } catch (ObjectLacksException ignored) {}
objectSave(user); plugin.getLog().info("after: " + user.getNodes().toString());
save(user);
});
return true; return true;
} }

View File

@ -39,7 +39,7 @@ import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
/** /**
* Provides a link between {@link PermissionHolder} and {@link me.lucko.luckperms.core.PermissionHolder} * Provides a link between {@link PermissionHolder} and {@link me.lucko.luckperms.core.PermissionHolder}
*/ */
@SuppressWarnings({"unused", "deprecation"}) @SuppressWarnings("unused")
@AllArgsConstructor @AllArgsConstructor
public class PermissionHolderLink implements PermissionHolder { public class PermissionHolderLink implements PermissionHolder {
@ -58,12 +58,12 @@ public class PermissionHolderLink implements PermissionHolder {
@Override @Override
public Set<Node> getEnduringPermissions() { public Set<Node> getEnduringPermissions() {
return Collections.unmodifiableSet(master.getNodes()); return master.getNodes();
} }
@Override @Override
public Set<Node> getTransientPermissions() { public Set<Node> getTransientPermissions() {
return Collections.unmodifiableSet(master.getTransientNodes()); return master.getTransientNodes();
} }
@Override @Override
@ -233,22 +233,36 @@ public class PermissionHolderLink implements PermissionHolder {
@Override @Override
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups, List<String> possibleNodes) { public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups, List<String> possibleNodes) {
return master.getLocalPermissions(server, world, excludedGroups, possibleNodes); Map<String, String> context = new HashMap<>();
if (server != null && !server.equals("")) {
context.put("server", server);
}
if (world != null && !world.equals("")) {
context.put("world", world);
}
return master.exportNodes(new Contexts(context, true, true, true, true, true), Collections.emptyList(), false);
} }
@Override @Override
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups) { public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups) {
return master.getLocalPermissions(server, world, excludedGroups); Map<String, String> context = new HashMap<>();
if (server != null && !server.equals("")) {
context.put("server", server);
}
if (world != null && !world.equals("")) {
context.put("world", world);
}
return master.exportNodes(new Contexts(context, true, true, true, true, true), Collections.emptyList(), false);
} }
@Override @Override
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups, List<String> possibleNodes) { public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups, List<String> possibleNodes) {
return master.getLocalPermissions(server, excludedGroups, possibleNodes); return getLocalPermissions(server, null, excludedGroups, possibleNodes);
} }
@Override @Override
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups) { public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups) {
return master.getLocalPermissions(server, excludedGroups); return getLocalPermissions(server, null, excludedGroups, null);
} }
@Override @Override

View File

@ -27,6 +27,8 @@ import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.*; import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission; import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.groups.Group;
import java.util.HashSet; import java.util.HashSet;
@ -55,6 +57,7 @@ public class GroupBulkChange extends SubCommand<Group> {
} }
Set<Node> toAdd = new HashSet<>(); Set<Node> toAdd = new HashSet<>();
Set<Node> toRemove = new HashSet<>();
if (!type.equals("world") && !type.equals("server")) { if (!type.equals("world") && !type.equals("server")) {
Message.BULK_CHANGE_TYPE_ERROR.send(sender); Message.BULK_CHANGE_TYPE_ERROR.send(sender);
@ -70,7 +73,7 @@ public class GroupBulkChange extends SubCommand<Group> {
continue; continue;
} }
iterator.remove(); toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setWorld(to).build()); toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setWorld(to).build());
} }
} else { } else {
@ -81,12 +84,23 @@ public class GroupBulkChange extends SubCommand<Group> {
continue; continue;
} }
iterator.remove(); toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setServer(to).build()); toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setServer(to).build());
} }
} }
group.getNodes().addAll(toAdd); toRemove.forEach(n -> {
try {
group.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
});
toAdd.forEach(n -> {
try {
group.setPermission(n);
} catch (ObjectAlreadyHasException ignored) {}
});
save(group, sender, plugin); save(group, sender, plugin);
Message.BULK_CHANGE_SUCCESS.send(sender, toAdd.size()); Message.BULK_CHANGE_SUCCESS.send(sender, toAdd.size());
return CommandResult.SUCCESS; return CommandResult.SUCCESS;

View File

@ -27,6 +27,8 @@ import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.*; import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission; import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
import java.util.HashSet; import java.util.HashSet;
@ -55,6 +57,7 @@ public class UserBulkChange extends SubCommand<User> {
} }
Set<Node> toAdd = new HashSet<>(); Set<Node> toAdd = new HashSet<>();
Set<Node> toRemove = new HashSet<>();
if (!type.equals("world") && !type.equals("server")) { if (!type.equals("world") && !type.equals("server")) {
Message.BULK_CHANGE_TYPE_ERROR.send(sender); Message.BULK_CHANGE_TYPE_ERROR.send(sender);
@ -75,7 +78,7 @@ public class UserBulkChange extends SubCommand<User> {
continue; continue;
} }
iterator.remove(); toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setWorld(to).build()); toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setWorld(to).build());
} }
} else { } else {
@ -91,12 +94,23 @@ public class UserBulkChange extends SubCommand<User> {
continue; continue;
} }
iterator.remove(); toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setServer(to).build()); toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setServer(to).build());
} }
} }
user.getNodes().addAll(toAdd); toRemove.forEach(n -> {
try {
user.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
});
toAdd.forEach(n -> {
try {
user.setPermission(n);
} catch (ObjectAlreadyHasException ignored) {}
});
save(user, sender, plugin); save(user, sender, plugin);
Message.BULK_CHANGE_SUCCESS.send(sender, toAdd.size()); Message.BULK_CHANGE_SUCCESS.send(sender, toAdd.size());
return CommandResult.SUCCESS; return CommandResult.SUCCESS;

View File

@ -27,6 +27,8 @@ import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.*; import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission; import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.storage.Datastore; import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
@ -70,6 +72,7 @@ public class BulkEditGroup extends SubCommand<Datastore> {
} }
Set<Node> toAdd = new HashSet<>(); Set<Node> toAdd = new HashSet<>();
Set<Node> toRemove = new HashSet<>();
Iterator<Node> iterator = user.getNodes().iterator(); Iterator<Node> iterator = user.getNodes().iterator();
if (type.equals("world")) { if (type.equals("world")) {
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -92,7 +95,7 @@ public class BulkEditGroup extends SubCommand<Datastore> {
continue; continue;
} }
iterator.remove(); toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setWorld(to).build()); toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setWorld(to).build());
} }
} else { } else {
@ -116,12 +119,23 @@ public class BulkEditGroup extends SubCommand<Datastore> {
continue; continue;
} }
iterator.remove(); toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setServer(to).build()); toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setServer(to).build());
} }
} }
user.getNodes().addAll(toAdd); toRemove.forEach(n -> {
try {
user.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
});
toAdd.forEach(n -> {
try {
user.setPermission(n);
} catch (ObjectAlreadyHasException ignored) {}
});
plugin.getUserManager().cleanup(user); plugin.getUserManager().cleanup(user);
plugin.getDatastore().saveUser(user); plugin.getDatastore().saveUser(user);
} }

View File

@ -27,6 +27,8 @@ import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.*; import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission; import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.storage.Datastore; import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
@ -69,6 +71,7 @@ public class BulkEditPermission extends SubCommand<Datastore> {
} }
Set<Node> toAdd = new HashSet<>(); Set<Node> toAdd = new HashSet<>();
Set<Node> toRemove = new HashSet<>();
Iterator<Node> iterator = user.getNodes().iterator(); Iterator<Node> iterator = user.getNodes().iterator();
if (type.equals("world")) { if (type.equals("world")) {
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -87,7 +90,7 @@ public class BulkEditPermission extends SubCommand<Datastore> {
continue; continue;
} }
iterator.remove(); toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setWorld(to).build()); toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setWorld(to).build());
} }
} else { } else {
@ -107,12 +110,23 @@ public class BulkEditPermission extends SubCommand<Datastore> {
continue; continue;
} }
iterator.remove(); toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setServer(to).build()); toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setServer(to).build());
} }
} }
user.getNodes().addAll(toAdd); toRemove.forEach(n -> {
try {
user.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
});
toAdd.forEach(n -> {
try {
user.setPermission(n);
} catch (ObjectAlreadyHasException ignored) {}
});
plugin.getUserManager().cleanup(user); plugin.getUserManager().cleanup(user);
plugin.getDatastore().saveUser(user); plugin.getDatastore().saveUser(user);
} }

View File

@ -22,6 +22,8 @@
package me.lucko.luckperms.core; package me.lucko.luckperms.core;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -37,11 +39,13 @@ import me.lucko.luckperms.contexts.Contexts;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException; import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.Cache;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -64,31 +68,120 @@ public abstract class PermissionHolder {
@Getter(AccessLevel.PROTECTED) @Getter(AccessLevel.PROTECTED)
private final LuckPermsPlugin plugin; private final LuckPermsPlugin plugin;
/** private final Set<Node> nodes = new HashSet<>();
* The user/group's permissions private final Set<Node> transientNodes = new HashSet<>();
*/
@Getter
private Set<Node> nodes = ConcurrentHashMap.newKeySet();
/** private Cache<SortedSet<LocalizedNode>> cache = new Cache<>();
* The user/group's transient permissions private Cache<SortedSet<LocalizedNode>> mergedCache = new Cache<>();
*/ private Cache<ImmutableSet<Node>> enduringCache = new Cache<>();
@Getter private Cache<ImmutableSet<Node>> transientCache = new Cache<>();
private Set<Node> transientNodes = ConcurrentHashMap.newKeySet();
@Getter @Getter
private final Lock ioLock = new ReentrantLock(); private final Lock ioLock = new ReentrantLock();
private void invalidateCache(boolean enduring) {
if (enduring) {
enduringCache.invalidate();
} else {
transientCache.invalidate();
}
cache.invalidate();
mergedCache.invalidate();
}
public Set<Node> getNodes() {
synchronized (nodes) {
return enduringCache.get(() -> ImmutableSet.copyOf(nodes));
}
}
public Set<Node> getTransientNodes() {
synchronized (transientNodes) {
return transientCache.get(() -> ImmutableSet.copyOf(transientNodes));
}
}
public void setNodes(Set<Node> nodes) {
synchronized (this.nodes) {
if (!this.nodes.equals(nodes)) {
invalidateCache(true);
}
this.nodes.clear();
this.nodes.addAll(nodes);
}
auditTemporaryPermissions();
}
public void setTransientNodes(Set<Node> nodes) {
synchronized (this.transientNodes) {
if (!this.transientNodes.equals(nodes)) {
invalidateCache(false);
}
this.transientNodes.clear();
this.transientNodes.addAll(nodes);
}
auditTemporaryPermissions();
}
@Deprecated
public void setNodes(Map<String, Boolean> nodes) {
synchronized (this.nodes) {
if (!this.nodes.equals(nodes)) {
invalidateCache(true);
}
this.nodes.clear();
this.nodes.addAll(nodes.entrySet().stream()
.map(e -> me.lucko.luckperms.core.Node.fromSerialisedNode(e.getKey(), e.getValue()))
.collect(Collectors.toList()));
}
auditTemporaryPermissions();
}
public void addNodeUnchecked(Node node) {
synchronized (nodes) {
nodes.add(node);
invalidateCache(true);
}
}
/** /**
* Returns a Set of nodes in priority order * Clear all of the holders permission nodes
*/
public void clearNodes() {
synchronized (nodes) {
nodes.clear();
invalidateCache(true);
}
}
public void clearTransientNodes() {
synchronized (transientNodes) {
transientNodes.clear();
invalidateCache(false);
}
}
/**
* Combines and returns this holders nodes in a priority order.
* @return the holders transient and permanent nodes * @return the holders transient and permanent nodes
*/ */
public SortedSet<LocalizedNode> getPermissions(boolean mergeTemp) { public SortedSet<LocalizedNode> getPermissions(boolean mergeTemp) {
// Returns no duplicate nodes. as in, nodes with the same value. Supplier<SortedSet<LocalizedNode>> supplier = () -> {
TreeSet<LocalizedNode> combined = new TreeSet<>(PriorityComparator.reverse()); TreeSet<LocalizedNode> combined = new TreeSet<>(PriorityComparator.reverse());
nodes.stream().map(n -> LocalizedNode.of(n, getObjectName())).forEach(combined::add);
transientNodes.stream().map(n -> LocalizedNode.of(n, getObjectName())).forEach(combined::add); getNodes().stream()
.map(n -> LocalizedNode.of(n, getObjectName()))
.forEach(combined::add);
getTransientNodes().stream()
.map(n -> LocalizedNode.of(n, getObjectName()))
.forEach(combined::add);
TreeSet<LocalizedNode> permissions = new TreeSet<>(PriorityComparator.reverse()); TreeSet<LocalizedNode> permissions = new TreeSet<>(PriorityComparator.reverse());
@ -104,6 +197,9 @@ public abstract class PermissionHolder {
} }
return permissions; return permissions;
};
return mergeTemp ? mergedCache.get(supplier) : cache.get(supplier);
} }
/** /**
@ -111,33 +207,44 @@ public abstract class PermissionHolder {
* @return true if permissions had expired and were removed * @return true if permissions had expired and were removed
*/ */
public boolean auditTemporaryPermissions() { public boolean auditTemporaryPermissions() {
final boolean[] work = {false}; boolean work = false;
final PermissionHolder instance = this; final PermissionHolder instance = this;
nodes.removeIf(node -> { synchronized (nodes) {
boolean w = nodes.removeIf(node -> {
if (node.hasExpired()) { if (node.hasExpired()) {
work[0] = true;
plugin.getApiProvider().fireEventAsync(new PermissionNodeExpireEvent(new PermissionHolderLink(instance), node)); plugin.getApiProvider().fireEventAsync(new PermissionNodeExpireEvent(new PermissionHolderLink(instance), node));
return true; return true;
} }
return false; return false;
}); });
if (w) {
invalidateCache(true);
work = true;
}
}
transientNodes.removeIf(node -> { synchronized (transientNodes) {
boolean w = transientNodes.removeIf(node -> {
if (node.hasExpired()) { if (node.hasExpired()) {
work[0] = true;
plugin.getApiProvider().fireEventAsync(new PermissionNodeExpireEvent(new PermissionHolderLink(instance), node)); plugin.getApiProvider().fireEventAsync(new PermissionNodeExpireEvent(new PermissionHolderLink(instance), node));
return true; return true;
} }
return false; return false;
}); });
if (w) {
invalidateCache(false);
work = true;
}
}
return work[0]; return work;
} }
/** /**
* Gets all of the nodes that this holder has and inherits * Resolves inherited nodes and returns them
* @param excludedGroups a list of groups to exclude * @param excludedGroups a list of groups to exclude
* @param context context to decide if groups should be applied
* @return a set of nodes * @return a set of nodes
*/ */
public SortedSet<LocalizedNode> getAllNodes(List<String> excludedGroups, Contexts context) { public SortedSet<LocalizedNode> getAllNodes(List<String> excludedGroups, Contexts context) {
@ -197,7 +304,6 @@ public abstract class PermissionHolder {
* @return a map of permissions * @return a map of permissions
*/ */
public Set<LocalizedNode> getAllNodesFiltered(Contexts context) { public Set<LocalizedNode> getAllNodesFiltered(Contexts context) {
Set<LocalizedNode> perms = ConcurrentHashMap.newKeySet();
SortedSet<LocalizedNode> allNodes; SortedSet<LocalizedNode> allNodes;
if (context.isApplyGroups()) { if (context.isApplyGroups()) {
@ -212,24 +318,19 @@ public abstract class PermissionHolder {
contexts.remove("server"); contexts.remove("server");
contexts.remove("world"); contexts.remove("world");
allNodes.removeIf(node ->
!node.shouldApplyOnServer(server, context.isIncludeGlobal(), plugin.getConfiguration().isApplyingRegex()) ||
!node.shouldApplyOnWorld(world, context.isIncludeGlobalWorld(), plugin.getConfiguration().isApplyingRegex()) ||
!node.shouldApplyWithContext(contexts, false)
);
Set<LocalizedNode> perms = ConcurrentHashMap.newKeySet();
all: all:
for (LocalizedNode ln : allNodes) { for (LocalizedNode ln : allNodes) {
Node node = ln.getNode();
if (!node.shouldApplyOnServer(server, context.isIncludeGlobal(), plugin.getConfiguration().isApplyingRegex())) {
continue;
}
if (!node.shouldApplyOnWorld(world, context.isIncludeGlobalWorld(), plugin.getConfiguration().isApplyingRegex())) {
continue;
}
if (!node.shouldApplyWithContext(contexts, false)) {
continue;
}
// Force higher priority nodes to override // Force higher priority nodes to override
for (LocalizedNode alreadyIn : perms) { for (LocalizedNode alreadyIn : perms) {
if (node.getPermission().equals(alreadyIn.getNode().getPermission())) { if (ln.getNode().getPermission().equals(alreadyIn.getNode().getPermission())) {
continue all; continue all;
} }
} }
@ -278,43 +379,7 @@ public abstract class PermissionHolder {
} }
} }
return Collections.unmodifiableMap(perms); return ImmutableMap.copyOf(perms);
}
public void setNodes(Set<Node> nodes) {
this.nodes.clear();
this.nodes.addAll(nodes);
auditTemporaryPermissions();
}
public void setTransiestNodes(Set<Node> nodes) {
this.transientNodes.clear();
this.transientNodes.addAll(nodes);
auditTemporaryPermissions();
}
public static Map<String, Boolean> exportToLegacy(Set<Node> nodes) {
Map<String, Boolean> m = new HashMap<>();
for (Node node : nodes) {
m.put(node.toSerializedNode(), node.getValue());
}
return Collections.unmodifiableMap(m);
}
// Convenience method
private static Node.Builder buildNode(String permission) {
return new me.lucko.luckperms.core.Node.Builder(permission);
}
@Deprecated
public void setNodes(Map<String, Boolean> nodes) {
this.nodes.clear();
this.nodes.addAll(nodes.entrySet().stream()
.map(e -> me.lucko.luckperms.core.Node.fromSerialisedNode(e.getKey(), e.getValue()))
.collect(Collectors.toList()));
auditTemporaryPermissions();
} }
/** /**
@ -324,7 +389,7 @@ public abstract class PermissionHolder {
* @return a tristate * @return a tristate
*/ */
public Tristate hasPermission(Node node, boolean t) { public Tristate hasPermission(Node node, boolean t) {
for (Node n : t ? transientNodes : nodes) { for (Node n : t ? getTransientNodes() : getNodes()) {
if (n.almostEquals(node)) { if (n.almostEquals(node)) {
return n.getTristate(); return n.getTristate();
} }
@ -419,7 +484,11 @@ public abstract class PermissionHolder {
throw new ObjectAlreadyHasException(); throw new ObjectAlreadyHasException();
} }
synchronized (nodes) {
nodes.add(node); nodes.add(node);
invalidateCache(true);
}
plugin.getApiProvider().fireEventAsync(new PermissionNodeSetEvent(new PermissionHolderLink(this), node)); plugin.getApiProvider().fireEventAsync(new PermissionNodeSetEvent(new PermissionHolderLink(this), node));
} }
@ -433,7 +502,11 @@ public abstract class PermissionHolder {
throw new ObjectAlreadyHasException(); throw new ObjectAlreadyHasException();
} }
synchronized (transientNodes) {
transientNodes.add(node); transientNodes.add(node);
invalidateCache(false);
}
plugin.getApiProvider().fireEventAsync(new PermissionNodeSetEvent(new PermissionHolderLink(this), node)); plugin.getApiProvider().fireEventAsync(new PermissionNodeSetEvent(new PermissionHolderLink(this), node));
} }
@ -471,7 +544,10 @@ public abstract class PermissionHolder {
throw new ObjectLacksException(); throw new ObjectLacksException();
} }
synchronized (nodes) {
nodes.removeIf(e -> e.almostEquals(node)); nodes.removeIf(e -> e.almostEquals(node));
invalidateCache(true);
}
if (node.isGroupNode()) { if (node.isGroupNode()) {
plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent(new PermissionHolderLink(this), plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent(new PermissionHolderLink(this),
@ -491,7 +567,10 @@ public abstract class PermissionHolder {
throw new ObjectLacksException(); throw new ObjectLacksException();
} }
synchronized (transientNodes) {
transientNodes.removeIf(e -> e.almostEquals(node)); transientNodes.removeIf(e -> e.almostEquals(node));
invalidateCache(false);
}
if (node.isGroupNode()) { if (node.isGroupNode()) {
plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent(new PermissionHolderLink(this), plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent(new PermissionHolderLink(this),
@ -578,43 +657,15 @@ public abstract class PermissionHolder {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
/* public static Map<String, Boolean> exportToLegacy(Set<Node> nodes) {
* Don't use these methods, only here for compat reasons ImmutableMap.Builder<String, Boolean> m = ImmutableMap.builder();
*/ for (Node node : nodes) {
m.put(node.toSerializedNode(), node.getValue());
@Deprecated
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups, List<String> possibleNodes) {
Map<String, String> context = new HashMap<>();
if (server != null && !server.equals("")) {
context.put("server", server);
} }
if (world != null && !world.equals("")) { return m.build();
context.put("world", world);
}
return exportNodes(new Contexts(context, plugin.getConfiguration().isIncludingGlobalPerms(), true, true, true, true), Collections.emptyList(), false);
} }
@Deprecated private static Node.Builder buildNode(String permission) {
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups) { return new me.lucko.luckperms.core.Node.Builder(permission);
Map<String, String> context = new HashMap<>();
if (server != null && !server.equals("")) {
context.put("server", server);
}
if (world != null && !world.equals("")) {
context.put("world", world);
}
return exportNodes(new Contexts(context, plugin.getConfiguration().isIncludingGlobalPerms(), true, true, true, true), Collections.emptyList(), false);
}
@SuppressWarnings("deprecation")
@Deprecated
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups, List<String> possibleNodes) {
return getLocalPermissions(server, null, excludedGroups, possibleNodes);
}
@SuppressWarnings("deprecation")
@Deprecated
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups) {
return getLocalPermissions(server, null, excludedGroups, null);
} }
} }

View File

@ -248,11 +248,4 @@ public class Group extends PermissionHolder implements Identifiable<String> {
public void unsetInheritGroup(Group group, String server, String world, boolean temporary) throws ObjectLacksException { public void unsetInheritGroup(Group group, String server, String world, boolean temporary) throws ObjectLacksException {
unsetPermission("group." + group.getName(), server, world, temporary); unsetPermission("group." + group.getName(), server, world, temporary);
} }
/**
* Clear all of the groups permission nodes
*/
public void clearNodes() {
getNodes().clear();
}
} }

View File

@ -79,6 +79,7 @@ public class JSONDatastore extends FlatfileDatastore {
public boolean loadUser(UUID uuid, String username) { public boolean loadUser(UUID uuid, String username) {
User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username)); User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username));
user.getIoLock().lock(); user.getIoLock().lock();
plugin.getLog().info("#loadUser for: " + user.getName());
try { try {
return call(() -> { return call(() -> {
File userFile = new File(usersDir, uuid.toString() + ".json"); File userFile = new File(usersDir, uuid.toString() + ".json");
@ -96,7 +97,7 @@ public class JSONDatastore extends FlatfileDatastore {
while (reader.hasNext()) { while (reader.hasNext()) {
String node = reader.nextName(); String node = reader.nextName();
boolean b = reader.nextBoolean(); boolean b = reader.nextBoolean();
user.getNodes().add(Node.fromSerialisedNode(node, b)); user.addNodeUnchecked(Node.fromSerialisedNode(node, b));
} }
reader.endObject(); reader.endObject();
reader.endObject(); reader.endObject();
@ -139,6 +140,7 @@ public class JSONDatastore extends FlatfileDatastore {
} }
}, false); }, false);
} finally { } finally {
plugin.getLog().info("#loadUser finished for: " + user.getName());
user.getIoLock().unlock(); user.getIoLock().unlock();
} }
} }
@ -146,6 +148,7 @@ public class JSONDatastore extends FlatfileDatastore {
@Override @Override
public boolean saveUser(User user) { public boolean saveUser(User user) {
user.getIoLock().lock(); user.getIoLock().lock();
plugin.getLog().info("#saveUser for: " + user.getName());
try { try {
return call(() -> { return call(() -> {
File userFile = new File(usersDir, user.getUuid().toString() + ".json"); File userFile = new File(usersDir, user.getUuid().toString() + ".json");
@ -173,6 +176,7 @@ public class JSONDatastore extends FlatfileDatastore {
writer.name("perms"); writer.name("perms");
writer.beginObject(); writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) { for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) {
plugin.getLog().info("entry: " + e.toString());
writer.name(e.getKey()).value(e.getValue().booleanValue()); writer.name(e.getKey()).value(e.getValue().booleanValue());
} }
writer.endObject(); writer.endObject();
@ -181,6 +185,7 @@ public class JSONDatastore extends FlatfileDatastore {
}); });
}, false); }, false);
} finally { } finally {
plugin.getLog().info("#saveUser ended for: " + user.getName());
user.getIoLock().unlock(); user.getIoLock().unlock();
} }
} }
@ -257,7 +262,7 @@ public class JSONDatastore extends FlatfileDatastore {
while (reader.hasNext()) { while (reader.hasNext()) {
String node = reader.nextName(); String node = reader.nextName();
boolean b = reader.nextBoolean(); boolean b = reader.nextBoolean();
group.getNodes().add(Node.fromSerialisedNode(node, b)); group.addNodeUnchecked(Node.fromSerialisedNode(node, b));
} }
reader.endObject(); reader.endObject();
@ -307,7 +312,7 @@ public class JSONDatastore extends FlatfileDatastore {
while (reader.hasNext()) { while (reader.hasNext()) {
String node = reader.nextName(); String node = reader.nextName();
boolean b = reader.nextBoolean(); boolean b = reader.nextBoolean();
group.getNodes().add(Node.fromSerialisedNode(node, b)); group.addNodeUnchecked(Node.fromSerialisedNode(node, b));
} }
reader.endObject(); reader.endObject();
reader.endObject(); reader.endObject();

View File

@ -92,7 +92,7 @@ public class YAMLDatastore extends FlatfileDatastore {
user.setPrimaryGroup((String) values.get("primary-group")); user.setPrimaryGroup((String) values.get("primary-group"));
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms"); Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
for (Map.Entry<String, Boolean> e : perms.entrySet()) { for (Map.Entry<String, Boolean> e : perms.entrySet()) {
user.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue())); user.addNodeUnchecked(Node.fromSerialisedNode(e.getKey(), e.getValue()));
} }
boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
@ -214,7 +214,7 @@ public class YAMLDatastore extends FlatfileDatastore {
return doRead(groupFile, values -> { return doRead(groupFile, values -> {
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms"); Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
for (Map.Entry<String, Boolean> e : perms.entrySet()) { for (Map.Entry<String, Boolean> e : perms.entrySet()) {
group.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue())); group.addNodeUnchecked(Node.fromSerialisedNode(e.getKey(), e.getValue()));
} }
return true; return true;
}); });
@ -247,7 +247,7 @@ public class YAMLDatastore extends FlatfileDatastore {
return groupFile.exists() && doRead(groupFile, values -> { return groupFile.exists() && doRead(groupFile, values -> {
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms"); Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
for (Map.Entry<String, Boolean> e : perms.entrySet()) { for (Map.Entry<String, Boolean> e : perms.entrySet()) {
group.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue())); group.addNodeUnchecked(Node.fromSerialisedNode(e.getKey(), e.getValue()));
} }
return true; return true;
}); });

View File

@ -249,8 +249,9 @@ public abstract class User extends PermissionHolder implements Identifiable<User
/** /**
* Clear all of the users permission nodes * Clear all of the users permission nodes
*/ */
@Override
public void clearNodes() { public void clearNodes() {
getNodes().clear(); super.clearNodes();
getPlugin().getUserManager().giveDefaultIfNeeded(this, false); getPlugin().getUserManager().giveDefaultIfNeeded(this, false);
} }
} }

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.utils;
import java.util.function.Supplier;
public class Cache<T> {
private T t = null;
public T get(Supplier<T> supplier) {
synchronized (this) {
if (t == null) {
t = supplier.get();
}
return t;
}
}
public void invalidate() {
synchronized (this) {
t = null;
}
}
}

View File

@ -23,17 +23,12 @@
package me.lucko.luckperms.api.sponge; package me.lucko.luckperms.api.sponge;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.contexts.Contexts;
import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.CommandSource;
@ -46,7 +41,6 @@ import org.spongepowered.api.util.Tristate;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters;
import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters; import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters;
@EqualsAndHashCode(of = {"holder"}) @EqualsAndHashCode(of = {"holder"})
@ -57,25 +51,28 @@ public class LuckPermsSubject implements Subject {
@Getter @Getter
private final PermissionHolder holder; private final PermissionHolder holder;
private final EnduringData enduringData; private final LuckPermsSubjectData enduringData;
private final TransientData transientData; private final LuckPermsSubjectData transientData;
protected final LuckPermsService service; protected final LuckPermsService service;
LuckPermsSubject(PermissionHolder holder, LuckPermsService service) { LuckPermsSubject(PermissionHolder holder, LuckPermsService service) {
this.holder = holder; this.holder = holder;
this.enduringData = new EnduringData(this, service, holder); this.enduringData = new LuckPermsSubjectData(true, this, service, holder);
this.transientData = new TransientData(service, holder); this.transientData = new LuckPermsSubjectData(true, this, service, holder);
this.service = service; this.service = service;
} }
private void objectSave(PermissionHolder t) { void objectSave(PermissionHolder t) {
service.getPlugin().doAsync(() -> {
if (t instanceof User) { if (t instanceof User) {
((User) t).refreshPermissions(); ((User) t).refreshPermissions();
service.getPlugin().getDatastore().saveUser(((User) t), Callback.empty()); service.getPlugin().getDatastore().saveUser(((User) t));
} }
if (t instanceof Group) { if (t instanceof Group) {
service.getPlugin().getDatastore().saveGroup(((Group) t), c -> service.getPlugin().runUpdateTask()); service.getPlugin().getDatastore().saveGroup(((Group) t));
service.getPlugin().runUpdateTask();
} }
});
} }
@Override @Override
@ -162,14 +159,14 @@ public class LuckPermsSubject implements Subject {
@Override @Override
public Optional<String> getOption(Set<Context> set, String s) { public Optional<String> getOption(Set<Context> set, String s) {
if (s.equalsIgnoreCase("prefix")) { if (s.equalsIgnoreCase("prefix")) {
String prefix = getChatMeta(true, holder); String prefix = getChatMeta(set, true, holder);
if (!prefix.equals("")) { if (!prefix.equals("")) {
return Optional.of(prefix); return Optional.of(prefix);
} }
} }
if (s.equalsIgnoreCase("suffix")) { if (s.equalsIgnoreCase("suffix")) {
String suffix = getChatMeta(false, holder); String suffix = getChatMeta(set, false, holder);
if (!suffix.equals("")) { if (!suffix.equals("")) {
return Optional.of(suffix); return Optional.of(suffix);
} }
@ -198,9 +195,15 @@ public class LuckPermsSubject implements Subject {
return SubjectData.GLOBAL_CONTEXT; return SubjectData.GLOBAL_CONTEXT;
} }
private String getChatMeta(boolean prefix, PermissionHolder holder) { private String getChatMeta(Set<Context> contexts, boolean prefix, PermissionHolder holder) {
if (holder == null) return ""; if (holder == null) return "";
Map<String, String> context = contexts.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
String server = context.get("server");
String world = context.get("world");
context.remove("server");
context.remove("world");
int priority = Integer.MIN_VALUE; int priority = Integer.MIN_VALUE;
String meta = null; String meta = null;
@ -213,15 +216,17 @@ public class LuckPermsSubject implements Subject {
continue; continue;
} }
if (!n.shouldApplyOnServer(service.getPlugin().getConfiguration().getVaultServer(), service.getPlugin().getConfiguration().isVaultIncludingGlobal(), false)) { if (!n.shouldApplyOnServer(server, service.getPlugin().getConfiguration().isVaultIncludingGlobal(), false)) {
continue; continue;
} }
/* TODO per world if (!n.shouldApplyOnWorld(world, true, false)) {
if (!n.shouldApplyOnWorld(world, service.getPlugin().getConfiguration().getVaultIncludeGlobal(), false)) { continue;
}
if (!n.shouldApplyWithContext(context, false)) {
continue; continue;
} }
*/
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix(); Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (value.getKey() > priority) { if (value.getKey() > priority) {
@ -232,650 +237,4 @@ public class LuckPermsSubject implements Subject {
return meta == null ? "" : unescapeCharacters(meta); return meta == null ? "" : unescapeCharacters(meta);
} }
@AllArgsConstructor
public static class EnduringData implements SubjectData {
private final LuckPermsSubject superClass;
private final LuckPermsService service;
@Getter
private final PermissionHolder holder;
@Override
public Map<Set<Context>, Map<String, Boolean>> getAllPermissions() {
Map<Set<Context>, Map<String, Boolean>> perms = new HashMap<>();
for (Node n : holder.getNodes()) {
Set<Context> contexts = n.getExtraContexts().entrySet().stream()
.map(entry -> new Context(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
}
if (n.isWorldSpecific()) {
contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
}
if (!perms.containsKey(contexts)) {
perms.put(contexts, new HashMap<>());
}
perms.get(contexts).put(n.getPermission(), n.getValue());
}
return ImmutableMap.copyOf(perms);
}
@Override
public Map<String, Boolean> getPermissions(Set<Context> set) {
return ImmutableMap.copyOf(getAllPermissions().getOrDefault(set, Collections.emptyMap()));
}
@Override
public boolean setPermission(Set<Context> set, String s, Tristate tristate) {
if (tristate == Tristate.UNDEFINED) {
// Unset
Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s);
for (Context ct : set) {
builder.withExtraContext(ct.getKey(), ct.getValue());
}
try {
holder.unsetPermission(builder.build());
} catch (ObjectLacksException ignored) {}
superClass.objectSave(holder);
return true;
}
Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s)
.setValue(tristate.asBoolean());
for (Context ct : set) {
builder.withExtraContext(ct.getKey(), ct.getValue());
}
try {
holder.setPermission(builder.build());
} catch (ObjectAlreadyHasException ignored) {}
superClass.objectSave(holder);
return true;
}
@Override
public boolean clearPermissions() {
holder.getNodes().clear();
if (holder instanceof User) {
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
}
superClass.objectSave(holder);
return true;
}
@Override
public boolean clearPermissions(Set<Context> set) {
Map<String, String> context = new HashMap<>();
for (Context c : set) {
context.put(c.getKey(), c.getValue());
}
boolean work = false;
Iterator<Node> iterator = holder.getNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (entry.shouldApplyWithContext(context)) {
iterator.remove();
work = true;
}
}
if (holder instanceof User) {
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
}
superClass.objectSave(holder);
return work;
}
@Override
public Map<Set<Context>, List<Subject>> getAllParents() {
Map<Set<Context>, List<Subject>> parents = new HashMap<>();
for (Node n : holder.getAllNodes(null, Contexts.allowAll())) {
if (!n.isGroupNode()) {
continue;
}
Set<Context> contexts = n.getExtraContexts().entrySet().stream()
.map(entry -> new Context(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
}
if (n.isWorldSpecific()) {
contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
}
if (!parents.containsKey(contexts)) {
parents.put(contexts, new ArrayList<>());
}
parents.get(contexts).add(service.getGroupSubjects().get(n.getGroupName()));
}
return ImmutableMap.copyOf(parents);
}
@Override
public List<Subject> getParents(Set<Context> contexts) {
return ImmutableList.copyOf(getAllParents().getOrDefault(contexts, Collections.emptyList()));
}
@Override
public boolean addParent(Set<Context> set, Subject subject) {
if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
try {
holder.setPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
} catch (ObjectAlreadyHasException ignored) {}
superClass.objectSave(holder);
} else {
return false;
}
return false;
}
@Override
public boolean removeParent(Set<Context> set, Subject subject) {
if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
try {
holder.unsetPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
} catch (ObjectLacksException ignored) {}
superClass.objectSave(holder);
} else {
return false;
}
return false;
}
@Override
public boolean clearParents() {
boolean work = false;
Iterator<Node> iterator = holder.getNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (entry.isGroupNode()) {
iterator.remove();
work = true;
}
}
if (holder instanceof User) {
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
}
superClass.objectSave(holder);
return work;
}
@Override
public boolean clearParents(Set<Context> set) {
Map<String, String> context = new HashMap<>();
for (Context c : set) {
context.put(c.getKey(), c.getValue());
}
boolean work = false;
Iterator<Node> iterator = holder.getNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (!entry.isGroupNode()) {
continue;
}
if (entry.shouldApplyWithContext(context)) {
iterator.remove();
work = true;
}
}
if (holder instanceof User) {
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
}
superClass.objectSave(holder);
return work;
}
@Override
public Map<Set<Context>, Map<String, String>> getAllOptions() {
Map<Set<Context>, Map<String, String>> options = new HashMap<>();
for (Node n : holder.getAllNodes(null, Contexts.allowAll())) {
if (!n.isMeta()) {
continue;
}
Set<Context> contexts = n.getExtraContexts().entrySet().stream()
.map(entry -> new Context(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
}
if (n.isWorldSpecific()) {
contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
}
if (!options.containsKey(contexts)) {
options.put(contexts, new HashMap<>());
}
options.get(contexts).put(unescapeCharacters(n.getMeta().getKey()), unescapeCharacters(n.getMeta().getValue()));
}
return ImmutableMap.copyOf(options);
}
@Override
public Map<String, String> getOptions(Set<Context> set) {
return ImmutableMap.copyOf(getAllOptions().getOrDefault(set, Collections.emptyMap()));
}
@Override
public boolean setOption(Set<Context> set, String key, String value) {
Map<String, String> context = new HashMap<>();
for (Context c : set) {
context.put(c.getKey(), c.getValue());
}
key = escapeCharacters(key);
value = escapeCharacters(value);
try {
holder.setPermission(new me.lucko.luckperms.core.Node.Builder("meta." + key + "." + value)
.withExtraContext(context)
.build()
);
} catch (ObjectAlreadyHasException ignored) {}
superClass.objectSave(holder);
return true;
}
@Override
public boolean clearOptions(Set<Context> set) {
Map<String, String> context = new HashMap<>();
for (Context c : set) {
context.put(c.getKey(), c.getValue());
}
boolean work = false;
Iterator<Node> iterator = holder.getNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (!entry.isMeta()) {
continue;
}
if (entry.shouldApplyWithContext(context)) {
iterator.remove();
work = true;
}
}
superClass.objectSave(holder);
return work;
}
@Override
public boolean clearOptions() {
boolean work = false;
Iterator<Node> iterator = holder.getNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (entry.isMeta()) {
iterator.remove();
work = true;
}
}
superClass.objectSave(holder);
return work;
}
}
@AllArgsConstructor
public static class TransientData implements SubjectData {
private final LuckPermsService service;
@Getter
private final PermissionHolder holder;
@Override
public Map<Set<Context>, Map<String, Boolean>> getAllPermissions() {
Map<Set<Context>, Map<String, Boolean>> perms = new HashMap<>();
for (Node n : holder.getTransientNodes()) {
Set<Context> contexts = n.getExtraContexts().entrySet().stream()
.map(entry -> new Context(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
}
if (n.isWorldSpecific()) {
contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
}
if (!perms.containsKey(contexts)) {
perms.put(contexts, new HashMap<>());
}
perms.get(contexts).put(n.getPermission(), n.getValue());
}
return ImmutableMap.copyOf(perms);
}
@Override
public Map<String, Boolean> getPermissions(Set<Context> set) {
return ImmutableMap.copyOf(getAllPermissions().getOrDefault(set, Collections.emptyMap()));
}
@Override
public boolean setPermission(Set<Context> set, String s, Tristate tristate) {
if (tristate == Tristate.UNDEFINED) {
// Unset
Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s);
for (Context ct : set) {
builder.withExtraContext(ct.getKey(), ct.getValue());
}
try {
holder.unsetTransientPermission(builder.build());
} catch (ObjectLacksException ignored) {}
return true;
}
Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s)
.setValue(tristate.asBoolean());
for (Context ct : set) {
builder.withExtraContext(ct.getKey(), ct.getValue());
}
try {
holder.setTransientPermission(builder.build());
} catch (ObjectAlreadyHasException ignored) {}
return true;
}
@Override
public boolean clearPermissions() {
holder.getTransientNodes().clear();
return true;
}
@Override
public boolean clearPermissions(Set<Context> set) {
Map<String, String> context = new HashMap<>();
for (Context c : set) {
context.put(c.getKey(), c.getValue());
}
boolean work = false;
Iterator<Node> iterator = holder.getTransientNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (entry.shouldApplyWithContext(context)) {
iterator.remove();
work = true;
}
}
return work;
}
@Override
public Map<Set<Context>, List<Subject>> getAllParents() {
Map<Set<Context>, List<Subject>> parents = new HashMap<>();
for (Node n : holder.getTransientNodes()) {
if (!n.isGroupNode()) {
continue;
}
Set<Context> contexts = n.getExtraContexts().entrySet().stream()
.map(entry -> new Context(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
}
if (n.isWorldSpecific()) {
contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
}
if (!parents.containsKey(contexts)) {
parents.put(contexts, new ArrayList<>());
}
parents.get(contexts).add(service.getGroupSubjects().get(n.getGroupName()));
}
return ImmutableMap.copyOf(parents);
}
@Override
public List<Subject> getParents(Set<Context> contexts) {
return ImmutableList.copyOf(getAllParents().getOrDefault(contexts, Collections.emptyList()));
}
@Override
public boolean addParent(Set<Context> set, Subject subject) {
if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
try {
holder.setTransientPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
} catch (ObjectAlreadyHasException ignored) {}
} else {
return false;
}
return false;
}
@Override
public boolean removeParent(Set<Context> set, Subject subject) {
if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
try {
holder.unsetTransientPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
} catch (ObjectLacksException ignored) {}
} else {
return false;
}
return false;
}
@Override
public boolean clearParents() {
boolean work = false;
Iterator<Node> iterator = holder.getTransientNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (entry.isGroupNode()) {
iterator.remove();
work = true;
}
}
return work;
}
@Override
public boolean clearParents(Set<Context> set) {
Map<String, String> context = new HashMap<>();
for (Context c : set) {
context.put(c.getKey(), c.getValue());
}
boolean work = false;
Iterator<Node> iterator = holder.getTransientNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (!entry.isGroupNode()) {
continue;
}
if (entry.shouldApplyWithContext(context)) {
iterator.remove();
work = true;
}
}
return work;
}
@Override
public Map<Set<Context>, Map<String, String>> getAllOptions() {
Map<Set<Context>, Map<String, String>> options = new HashMap<>();
for (Node n : holder.getTransientNodes()) {
if (!n.isMeta()) {
continue;
}
Set<Context> contexts = n.getExtraContexts().entrySet().stream()
.map(entry -> new Context(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
}
if (n.isWorldSpecific()) {
contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
}
if (!options.containsKey(contexts)) {
options.put(contexts, new HashMap<>());
}
options.get(contexts).put(unescapeCharacters(n.getMeta().getKey()), unescapeCharacters(n.getMeta().getValue()));
}
return ImmutableMap.copyOf(options);
}
@Override
public Map<String, String> getOptions(Set<Context> set) {
return ImmutableMap.copyOf(getAllOptions().getOrDefault(set, Collections.emptyMap()));
}
@Override
public boolean setOption(Set<Context> set, String key, String value) {
Map<String, String> context = new HashMap<>();
for (Context c : set) {
context.put(c.getKey(), c.getValue());
}
key = escapeCharacters(key);
value = escapeCharacters(value);
try {
holder.setTransientPermission(new me.lucko.luckperms.core.Node.Builder("meta." + key + "." + value)
.withExtraContext(context)
.build()
);
} catch (ObjectAlreadyHasException ignored) {}
return true;
}
@Override
public boolean clearOptions(Set<Context> set) {
Map<String, String> context = new HashMap<>();
for (Context c : set) {
context.put(c.getKey(), c.getValue());
}
boolean work = false;
Iterator<Node> iterator = holder.getTransientNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (!entry.isMeta()) {
continue;
}
if (entry.shouldApplyWithContext(context)) {
iterator.remove();
work = true;
}
}
return work;
}
@Override
public boolean clearOptions() {
boolean work = false;
Iterator<Node> iterator = holder.getTransientNodes().iterator();
while (iterator.hasNext()) {
Node entry = iterator.next();
if (entry.isMeta()) {
iterator.remove();
work = true;
}
}
return work;
}
}
} }

View File

@ -0,0 +1,412 @@
/*
* 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.sponge;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.users.User;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;
import java.util.*;
import java.util.stream.Collectors;
import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters;
import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters;
@AllArgsConstructor
public class LuckPermsSubjectData implements SubjectData {
private final boolean enduring;
private final LuckPermsSubject superClass;
private final LuckPermsService service;
@Getter
private final PermissionHolder holder;
@Override
public Map<Set<Context>, Map<String, Boolean>> getAllPermissions() {
Map<Set<Context>, Map<String, Boolean>> perms = new HashMap<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
Set<Context> contexts = n.getExtraContexts().entrySet().stream()
.map(entry -> new Context(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
}
if (n.isWorldSpecific()) {
contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
}
if (!perms.containsKey(contexts)) {
perms.put(contexts, new HashMap<>());
}
perms.get(contexts).put(n.getPermission(), n.getValue());
}
ImmutableMap.Builder<Set<Context>, Map<String, Boolean>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : perms.entrySet()) {
map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue()));
}
return map.build();
}
@Override
public Map<String, Boolean> getPermissions(Set<Context> set) {
return getAllPermissions().getOrDefault(set, ImmutableMap.of());
}
@Override
public boolean setPermission(Set<Context> set, String s, Tristate tristate) {
if (tristate == Tristate.UNDEFINED) {
// Unset
Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s);
for (Context ct : set) {
builder.withExtraContext(ct.getKey(), ct.getValue());
}
try {
if (enduring) {
holder.unsetPermission(builder.build());
} else {
holder.unsetTransientPermission(builder.build());
}
} catch (ObjectLacksException ignored) {}
superClass.objectSave(holder);
return true;
}
Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s)
.setValue(tristate.asBoolean());
for (Context ct : set) {
builder.withExtraContext(ct.getKey(), ct.getValue());
}
try {
if (enduring) {
holder.setPermission(builder.build());
} else {
holder.setTransientPermission(builder.build());
}
} catch (ObjectAlreadyHasException ignored) {}
superClass.objectSave(holder);
return true;
}
@Override
public boolean clearPermissions() {
if (enduring) {
holder.clearNodes();
} else {
holder.clearTransientNodes();
}
superClass.objectSave(holder);
return true;
}
@Override
public boolean clearPermissions(Set<Context> set) {
Map<String, String> context = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
List<Node> toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream()
.filter(node -> node.shouldApplyWithContext(context))
.collect(Collectors.toList());
toRemove.forEach(n -> {
try {
if (enduring) {
holder.unsetPermission(n);
} else {
holder.unsetTransientPermission(n);
}
} catch (ObjectLacksException ignored) {}
});
if (holder instanceof User) {
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
}
superClass.objectSave(holder);
return !toRemove.isEmpty();
}
@Override
public Map<Set<Context>, List<Subject>> getAllParents() {
Map<Set<Context>, List<Subject>> parents = new HashMap<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
if (!n.isGroupNode()) {
continue;
}
Set<Context> contexts = n.getExtraContexts().entrySet().stream()
.map(entry -> new Context(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
}
if (n.isWorldSpecific()) {
contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
}
if (!parents.containsKey(contexts)) {
parents.put(contexts, new ArrayList<>());
}
parents.get(contexts).add(service.getGroupSubjects().get(n.getGroupName()));
}
ImmutableMap.Builder<Set<Context>, List<Subject>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, List<Subject>> e : parents.entrySet()) {
map.put(ImmutableSet.copyOf(e.getKey()), ImmutableList.copyOf(e.getValue()));
}
return map.build();
}
@Override
public List<Subject> getParents(Set<Context> contexts) {
return getAllParents().getOrDefault(contexts, ImmutableList.of());
}
@Override
public boolean addParent(Set<Context> set, Subject subject) {
if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
try {
if (enduring) {
holder.setPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
} else {
holder.setTransientPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
}
} catch (ObjectAlreadyHasException ignored) {}
superClass.objectSave(holder);
} else {
return false;
}
return false;
}
@Override
public boolean removeParent(Set<Context> set, Subject subject) {
if (subject instanceof LuckPermsSubject) {
LuckPermsSubject permsSubject = ((LuckPermsSubject) subject);
Map<String, String> contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
try {
if (enduring) {
holder.unsetPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
} else {
holder.unsetTransientPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
}
} catch (ObjectLacksException ignored) {}
superClass.objectSave(holder);
} else {
return false;
}
return false;
}
@Override
public boolean clearParents() {
List<Node> toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream()
.filter(Node::isGroupNode)
.collect(Collectors.toList());
toRemove.forEach(n -> {
try {
if (enduring) {
holder.unsetPermission(n);
} else {
holder.unsetTransientPermission(n);
}
} catch (ObjectLacksException ignored) {}
});
if (holder instanceof User) {
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
}
superClass.objectSave(holder);
return !toRemove.isEmpty();
}
@Override
public boolean clearParents(Set<Context> set) {
Map<String, String> context = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
List<Node> toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream()
.filter(Node::isGroupNode)
.filter(node -> node.shouldApplyWithContext(context))
.collect(Collectors.toList());
toRemove.forEach(n -> {
try {
if (enduring) {
holder.unsetPermission(n);
} else {
holder.unsetTransientPermission(n);
}
} catch (ObjectLacksException ignored) {}
});
if (holder instanceof User) {
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
}
superClass.objectSave(holder);
return !toRemove.isEmpty();
}
@Override
public Map<Set<Context>, Map<String, String>> getAllOptions() {
Map<Set<Context>, Map<String, String>> options = new HashMap<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) {
continue;
}
Set<Context> contexts = n.getExtraContexts().entrySet().stream()
.map(entry -> new Context(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
}
if (n.isWorldSpecific()) {
contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
}
if (!options.containsKey(contexts)) {
options.put(contexts, new HashMap<>());
}
options.get(contexts).put(unescapeCharacters(n.getMeta().getKey()), unescapeCharacters(n.getMeta().getValue()));
}
ImmutableMap.Builder<Set<Context>, Map<String, String>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, Map<String, String>> e : options.entrySet()) {
map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue()));
}
return map.build();
}
@Override
public Map<String, String> getOptions(Set<Context> set) {
return getAllOptions().getOrDefault(set, Collections.emptyMap());
}
@Override
public boolean setOption(Set<Context> set, String key, String value) {
Map<String, String> context = new HashMap<>();
for (Context c : set) {
context.put(c.getKey(), c.getValue());
}
key = escapeCharacters(key);
value = escapeCharacters(value);
try {
if (enduring) {
holder.setPermission(new me.lucko.luckperms.core.Node.Builder("meta." + key + "." + value)
.withExtraContext(context)
.build()
);
} else {
holder.setTransientPermission(new me.lucko.luckperms.core.Node.Builder("meta." + key + "." + value)
.withExtraContext(context)
.build()
);
}
} catch (ObjectAlreadyHasException ignored) {}
superClass.objectSave(holder);
return true;
}
@Override
public boolean clearOptions(Set<Context> set) {
Map<String, String> context = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue));
List<Node> toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream()
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
.filter(node -> node.shouldApplyWithContext(context))
.collect(Collectors.toList());
toRemove.forEach(n -> {
try {
if (enduring) {
holder.unsetPermission(n);
} else {
holder.unsetTransientPermission(n);
}
} catch (ObjectLacksException ignored) {}
});
superClass.objectSave(holder);
return !toRemove.isEmpty();
}
@Override
public boolean clearOptions() {
List<Node> toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream()
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
.collect(Collectors.toList());
toRemove.forEach(n -> {
try {
if (enduring) {
holder.unsetPermission(n);
} else {
holder.unsetTransientPermission(n);
}
} catch (ObjectLacksException ignored) {}
});
superClass.objectSave(holder);
return !toRemove.isEmpty();
}
}