Cache SubjectReference instances, general cleanup
This commit is contained in:
parent
f9efa15781
commit
777c972bdc
@ -106,6 +106,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LuckPerms implementation for the Bukkit API.
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ import net.md_5.bungee.api.plugin.TabExecutor;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
class BungeeCommand extends Command implements TabExecutor {
|
public class BungeeCommand extends Command implements TabExecutor {
|
||||||
private final LPBungeePlugin plugin;
|
private final LPBungeePlugin plugin;
|
||||||
private final CommandManager manager;
|
private final CommandManager manager;
|
||||||
|
|
||||||
|
@ -91,6 +91,9 @@ import java.util.UUID;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LuckPerms implementation for the BungeeCord API.
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ public final class GroupDelegate extends PermissionHolderDelegate implements Gro
|
|||||||
if (!(o instanceof GroupDelegate)) return false;
|
if (!(o instanceof GroupDelegate)) return false;
|
||||||
|
|
||||||
GroupDelegate other = (GroupDelegate) o;
|
GroupDelegate other = (GroupDelegate) o;
|
||||||
return this.getName() == null ? other.getName() == null : this.getName().equals(other.getName());
|
return this.getName().equals(other.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
@ -117,7 +117,7 @@ public final class TrackDelegate implements Track {
|
|||||||
if (!(o instanceof TrackDelegate)) return false;
|
if (!(o instanceof TrackDelegate)) return false;
|
||||||
|
|
||||||
TrackDelegate other = (TrackDelegate) o;
|
TrackDelegate other = (TrackDelegate) o;
|
||||||
return this.getName() == null ? other.getName() == null : this.getName().equals(other.getName());
|
return this.getName().equals(other.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
@ -228,7 +228,7 @@ public final class UserDelegate extends PermissionHolderDelegate implements User
|
|||||||
if (!(o instanceof UserDelegate)) return false;
|
if (!(o instanceof UserDelegate)) return false;
|
||||||
|
|
||||||
UserDelegate other = (UserDelegate) o;
|
UserDelegate other = (UserDelegate) o;
|
||||||
return this.getUuid() == null ? other.getUuid() == null : this.getUuid().equals(other.getUuid());
|
return this.getUuid().equals(other.getUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
@ -66,8 +66,8 @@ public final class ImmutableNode implements Node {
|
|||||||
/*
|
/*
|
||||||
* NODE STATE
|
* NODE STATE
|
||||||
*
|
*
|
||||||
* This are the actual node parameters, and are
|
* This are the actual node attributes, and are
|
||||||
* basically what this class wraps.
|
* really just what this class wraps.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -83,53 +83,6 @@ public class MongoDBBacking extends AbstractBacking {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MongoDB does not allow '.' or '$' in key names.
|
|
||||||
See: https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
|
|
||||||
The following two methods convert the node maps so they can be stored. */
|
|
||||||
|
|
||||||
private static final Function<String, String> CONVERT_STRING = s -> s.replace(".", "[**DOT**]").replace("$", "[**DOLLAR**]");
|
|
||||||
private static final Function<String, String> REVERT_STRING = s -> s.replace("[**DOT**]", ".").replace("[**DOLLAR**]", "$");
|
|
||||||
|
|
||||||
private static <V> Map<String, V> convert(Map<String, V> map) {
|
|
||||||
return map.entrySet().stream()
|
|
||||||
.collect(Collectors.toMap(e -> CONVERT_STRING.apply(e.getKey()), Map.Entry::getValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <V> Map<String, V> revert(Map<String, V> map) {
|
|
||||||
return map.entrySet().stream()
|
|
||||||
.collect(Collectors.toMap(e -> REVERT_STRING.apply(e.getKey()), Map.Entry::getValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Document fromUser(User user) {
|
|
||||||
Document main = new Document("_id", user.getUuid())
|
|
||||||
.append("name", user.getName().orElse("null"))
|
|
||||||
.append("primaryGroup", user.getPrimaryGroup().getStoredValue());
|
|
||||||
|
|
||||||
Document perms = new Document();
|
|
||||||
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(user.getEnduringNodes().values())).entrySet()) {
|
|
||||||
perms.append(e.getKey(), e.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
main.append("perms", perms);
|
|
||||||
return main;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Document fromGroup(Group group) {
|
|
||||||
Document main = new Document("_id", group.getName());
|
|
||||||
|
|
||||||
Document perms = new Document();
|
|
||||||
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(group.getEnduringNodes().values())).entrySet()) {
|
|
||||||
perms.append(e.getKey(), e.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
main.append("perms", perms);
|
|
||||||
return main;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Document fromTrack(Track track) {
|
|
||||||
return new Document("_id", track.getName()).append("groups", track.getGroups());
|
|
||||||
}
|
|
||||||
|
|
||||||
private final DatastoreConfiguration configuration;
|
private final DatastoreConfiguration configuration;
|
||||||
private MongoClient mongoClient;
|
private MongoClient mongoClient;
|
||||||
private MongoDatabase database;
|
private MongoDatabase database;
|
||||||
@ -752,6 +705,53 @@ public class MongoDBBacking extends AbstractBacking {
|
|||||||
}, null);
|
}, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MongoDB does not allow '.' or '$' in key names.
|
||||||
|
See: https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
|
||||||
|
The following two methods convert the node maps so they can be stored. */
|
||||||
|
|
||||||
|
private static final Function<String, String> CONVERT_STRING = s -> s.replace(".", "[**DOT**]").replace("$", "[**DOLLAR**]");
|
||||||
|
private static final Function<String, String> REVERT_STRING = s -> s.replace("[**DOT**]", ".").replace("[**DOLLAR**]", "$");
|
||||||
|
|
||||||
|
private static <V> Map<String, V> convert(Map<String, V> map) {
|
||||||
|
return map.entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(e -> CONVERT_STRING.apply(e.getKey()), Map.Entry::getValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <V> Map<String, V> revert(Map<String, V> map) {
|
||||||
|
return map.entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(e -> REVERT_STRING.apply(e.getKey()), Map.Entry::getValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Document fromUser(User user) {
|
||||||
|
Document main = new Document("_id", user.getUuid())
|
||||||
|
.append("name", user.getName().orElse("null"))
|
||||||
|
.append("primaryGroup", user.getPrimaryGroup().getStoredValue());
|
||||||
|
|
||||||
|
Document perms = new Document();
|
||||||
|
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(user.getEnduringNodes().values())).entrySet()) {
|
||||||
|
perms.append(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
main.append("perms", perms);
|
||||||
|
return main;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Document fromGroup(Group group) {
|
||||||
|
Document main = new Document("_id", group.getName());
|
||||||
|
|
||||||
|
Document perms = new Document();
|
||||||
|
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(group.getEnduringNodes().values())).entrySet()) {
|
||||||
|
perms.append(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
main.append("perms", perms);
|
||||||
|
return main;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Document fromTrack(Track track) {
|
||||||
|
return new Document("_id", track.getName()).append("groups", track.getGroups());
|
||||||
|
}
|
||||||
|
|
||||||
public static Map<String, Boolean> exportToLegacy(Iterable<Node> nodes) {
|
public static Map<String, Boolean> exportToLegacy(Iterable<Node> nodes) {
|
||||||
Map<String, Boolean> m = new HashMap<>();
|
Map<String, Boolean> m = new HashMap<>();
|
||||||
for (Node node : nodes) {
|
for (Node node : nodes) {
|
||||||
|
@ -33,6 +33,11 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
|||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System wide update task for LuckPerms.
|
||||||
|
*
|
||||||
|
* <p>Ensures that all local data is consistent with the storage.</p>
|
||||||
|
*/
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class UpdateTask implements Runnable {
|
public class UpdateTask implements Runnable {
|
||||||
private final LuckPermsPlugin plugin;
|
private final LuckPermsPlugin plugin;
|
||||||
@ -43,7 +48,9 @@ public class UpdateTask implements Runnable {
|
|||||||
private final boolean initialUpdate;
|
private final boolean initialUpdate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called ASYNC
|
* Runs the update task
|
||||||
|
*
|
||||||
|
* <p>Called <b>async</b>.</p>
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -72,6 +72,7 @@ public class PermissionVault implements Runnable {
|
|||||||
for (String e; (e = queue.poll()) != null; ) {
|
for (String e; (e = queue.poll()) != null; ) {
|
||||||
try {
|
try {
|
||||||
String s = e.toLowerCase();
|
String s = e.toLowerCase();
|
||||||
|
// only attempt an insert if we're not seen this permission before
|
||||||
if (knownPermissions.add(s)) {
|
if (knownPermissions.add(s)) {
|
||||||
insert(s);
|
insert(s);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,9 @@ import com.zaxxer.hikari.HikariDataSource;
|
|||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple hikari wrapper
|
||||||
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class HikariSupplier implements AutoCloseable {
|
public class HikariSupplier implements AutoCloseable {
|
||||||
|
|
||||||
|
@ -39,12 +39,24 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads content to GitHub's GIST service.
|
||||||
|
*/
|
||||||
public class PasteUtils {
|
public class PasteUtils {
|
||||||
|
private static final String GIST_API = "https://api.github.com/gists";
|
||||||
|
private static final String SHORTEN_API = "https://git.io";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads content to GIST, and returns a shortened URL.
|
||||||
|
*
|
||||||
|
* @param desc the description of the gist
|
||||||
|
* @param files the files to include in the gist (file name --> content)
|
||||||
|
* @return the url, or null
|
||||||
|
*/
|
||||||
public static String paste(String desc, List<Map.Entry<String, String>> files) {
|
public static String paste(String desc, List<Map.Entry<String, String>> files) {
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
try {
|
try {
|
||||||
connection = (HttpURLConnection) new URL("https://api.github.com/gists").openConnection();
|
connection = (HttpURLConnection) new URL(GIST_API).openConnection();
|
||||||
connection.setRequestMethod("POST");
|
connection.setRequestMethod("POST");
|
||||||
connection.setDoInput(true);
|
connection.setDoInput(true);
|
||||||
connection.setDoOutput(true);
|
connection.setDoOutput(true);
|
||||||
@ -82,7 +94,7 @@ public class PasteUtils {
|
|||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
connection = (HttpURLConnection) new URL("https://git.io").openConnection();
|
connection = (HttpURLConnection) new URL(SHORTEN_API).openConnection();
|
||||||
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||||
connection.setDoOutput(true);
|
connection.setDoOutput(true);
|
||||||
try (OutputStream os = connection.getOutputStream()) {
|
try (OutputStream os = connection.getOutputStream()) {
|
||||||
|
@ -66,7 +66,7 @@ public class Predicates {
|
|||||||
public static <T> Predicate<T> isOneOf(Set<T> ta) {
|
public static <T> Predicate<T> isOneOf(Set<T> ta) {
|
||||||
return t -> {
|
return t -> {
|
||||||
for (T i : ta) {
|
for (T i : ta) {
|
||||||
if (i == t) {
|
if (i.equals(t)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ public class Predicates {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Predicate<T> is(T t) {
|
public static <T> Predicate<T> is(T t) {
|
||||||
return t2 -> t == t2;
|
return t::equals;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Predicate<T> inverse(Predicate<T> t) {
|
public static <T> Predicate<T> inverse(Predicate<T> t) {
|
||||||
|
@ -30,6 +30,9 @@ import lombok.experimental.UtilityClass;
|
|||||||
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngine;
|
||||||
import javax.script.ScriptEngineManager;
|
import javax.script.ScriptEngineManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nashorn provider utility
|
||||||
|
*/
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class Scripting {
|
public class Scripting {
|
||||||
private static ScriptEngine SCRIPT_ENGINE = null;
|
private static ScriptEngine SCRIPT_ENGINE = null;
|
||||||
|
@ -171,6 +171,8 @@ public class VerboseListener {
|
|||||||
/**
|
/**
|
||||||
* Uploads the captured data in this listener to a paste and returns the url
|
* Uploads the captured data in this listener to a paste and returns the url
|
||||||
*
|
*
|
||||||
|
* @param showTraces if stack traces should be included in the output
|
||||||
|
* @param attachRaw if the rawdata should be attached to the gist
|
||||||
* @return the url
|
* @return the url
|
||||||
* @see PasteUtils#paste(String, List)
|
* @see PasteUtils#paste(String, List)
|
||||||
*/
|
*/
|
||||||
|
@ -33,6 +33,7 @@ import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
|||||||
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
||||||
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
|
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||||
|
import me.lucko.luckperms.sponge.service.model.SubjectReferenceFactory;
|
||||||
|
|
||||||
import org.spongepowered.api.service.context.Context;
|
import org.spongepowered.api.service.context.Context;
|
||||||
import org.spongepowered.api.service.permission.SubjectData;
|
import org.spongepowered.api.service.permission.SubjectData;
|
||||||
@ -111,12 +112,12 @@ public class SubjectDataProxy implements SubjectData {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> addParent(Set<Context> contexts, org.spongepowered.api.service.permission.SubjectReference ref) {
|
public CompletableFuture<Boolean> addParent(Set<Context> contexts, org.spongepowered.api.service.permission.SubjectReference ref) {
|
||||||
return getHandle().thenCompose(handle -> handle.addParent(CompatibilityUtil.convertContexts(contexts), SubjectReference.cast(service, ref)));
|
return getHandle().thenCompose(handle -> handle.addParent(CompatibilityUtil.convertContexts(contexts), SubjectReferenceFactory.obtain(service, ref)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> removeParent(Set<Context> contexts, org.spongepowered.api.service.permission.SubjectReference ref) {
|
public CompletableFuture<Boolean> removeParent(Set<Context> contexts, org.spongepowered.api.service.permission.SubjectReference ref) {
|
||||||
return getHandle().thenCompose(handle -> handle.removeParent(CompatibilityUtil.convertContexts(contexts), SubjectReference.cast(service, ref)));
|
return getHandle().thenCompose(handle -> handle.removeParent(CompatibilityUtil.convertContexts(contexts), SubjectReferenceFactory.obtain(service, ref)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -32,6 +32,7 @@ import me.lucko.luckperms.sponge.service.model.CompatibilityUtil;
|
|||||||
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
||||||
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||||
|
import me.lucko.luckperms.sponge.service.model.SubjectReferenceFactory;
|
||||||
|
|
||||||
import org.spongepowered.api.command.CommandSource;
|
import org.spongepowered.api.command.CommandSource;
|
||||||
import org.spongepowered.api.service.context.Context;
|
import org.spongepowered.api.service.context.Context;
|
||||||
@ -109,14 +110,14 @@ public class SubjectProxy implements Subject {
|
|||||||
@Override
|
@Override
|
||||||
public boolean isChildOf(org.spongepowered.api.service.permission.SubjectReference parent) {
|
public boolean isChildOf(org.spongepowered.api.service.permission.SubjectReference parent) {
|
||||||
return getHandle().thenApply(handle -> {
|
return getHandle().thenApply(handle -> {
|
||||||
return handle.isChildOf(ImmutableContextSet.empty(), SubjectReference.cast(service, parent));
|
return handle.isChildOf(ImmutableContextSet.empty(), SubjectReferenceFactory.obtain(service, parent));
|
||||||
}).join();
|
}).join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isChildOf(Set<Context> contexts, org.spongepowered.api.service.permission.SubjectReference parent) {
|
public boolean isChildOf(Set<Context> contexts, org.spongepowered.api.service.permission.SubjectReference parent) {
|
||||||
return getHandle().thenApply(handle -> {
|
return getHandle().thenApply(handle -> {
|
||||||
return handle.isChildOf(CompatibilityUtil.convertContexts(contexts), SubjectReference.cast(service, parent));
|
return handle.isChildOf(CompatibilityUtil.convertContexts(contexts), SubjectReferenceFactory.obtain(service, parent));
|
||||||
}).join();
|
}).join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ import org.spongepowered.api.service.context.Context;
|
|||||||
import org.spongepowered.api.util.Tristate;
|
import org.spongepowered.api.util.Tristate;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class for converting between Sponge and LuckPerms context and tristate classes
|
* Utility class for converting between Sponge and LuckPerms context and tristate classes
|
||||||
@ -47,9 +48,11 @@ import java.util.Set;
|
|||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class CompatibilityUtil {
|
public class CompatibilityUtil {
|
||||||
private static final LoadingCache<Set<Context>, ImmutableContextSet> SPONGE_TO_LP_CACHE = Caffeine.newBuilder()
|
private static final LoadingCache<Set<Context>, ImmutableContextSet> SPONGE_TO_LP_CACHE = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||||
.build(ImmutableContextSet::fromEntries);
|
.build(ImmutableContextSet::fromEntries);
|
||||||
|
|
||||||
private static final LoadingCache<ImmutableContextSet, ImmutableSet<Context>> LP_TO_SPONGE_CACHE = Caffeine.newBuilder()
|
private static final LoadingCache<ImmutableContextSet, ImmutableSet<Context>> LP_TO_SPONGE_CACHE = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||||
.build(set -> set.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(ImmutableCollectors.toImmutableSet()));
|
.build(set -> set.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(ImmutableCollectors.toImmutableSet()));
|
||||||
|
|
||||||
public static ImmutableContextSet convertContexts(@NonNull Set<Context> contexts) {
|
public static ImmutableContextSet convertContexts(@NonNull Set<Context> contexts) {
|
||||||
|
@ -31,10 +31,12 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
import me.lucko.luckperms.api.Tristate;
|
import me.lucko.luckperms.api.Tristate;
|
||||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||||
|
|
||||||
|
import org.spongepowered.api.service.permission.SubjectData;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LuckPerms model for the Sponge {@link org.spongepowered.api.service.permission.SubjectData}
|
* LuckPerms model for the Sponge {@link SubjectData}
|
||||||
*/
|
*/
|
||||||
public interface LPSubjectData {
|
public interface LPSubjectData {
|
||||||
|
|
||||||
|
@ -25,103 +25,135 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.sponge.service.model;
|
package me.lucko.luckperms.sponge.service.model;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.ToString;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import org.spongepowered.api.service.permission.Subject;
|
import org.spongepowered.api.service.permission.Subject;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ToString(of = {"collectionIdentifier", "subjectIdentifier"})
|
import javax.annotation.Nonnull;
|
||||||
@EqualsAndHashCode(of = {"collectionIdentifier", "subjectIdentifier"})
|
|
||||||
@RequiredArgsConstructor(staticName = "of")
|
/**
|
||||||
|
* Represents a reference to a given Subject.
|
||||||
|
*
|
||||||
|
* Use of this class (or interface) should have no negative impact on
|
||||||
|
* performance, as {@link #resolve()} calls are cached.
|
||||||
|
*/
|
||||||
public final class SubjectReference implements org.spongepowered.api.service.permission.SubjectReference {
|
public final class SubjectReference implements org.spongepowered.api.service.permission.SubjectReference {
|
||||||
|
|
||||||
@Deprecated
|
/**
|
||||||
public static SubjectReference deserialize(LPPermissionService service, String s) {
|
* The time a subject instance should be cached in this reference
|
||||||
List<String> parts = Splitter.on('/').limit(2).splitToList(s);
|
*/
|
||||||
return of(service, parts.get(0), parts.get(1));
|
private static final long CACHE_TIME = TimeUnit.SECONDS.toMillis(60);
|
||||||
}
|
|
||||||
|
|
||||||
public static SubjectReference of(LPPermissionService service, Subject subject) {
|
|
||||||
return of(service, subject.getContainingCollection().getIdentifier(), subject.getIdentifier());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SubjectReference cast(LPPermissionService service, org.spongepowered.api.service.permission.SubjectReference reference) {
|
|
||||||
if (reference instanceof SubjectReference) {
|
|
||||||
return ((SubjectReference) reference);
|
|
||||||
} else {
|
|
||||||
return of(service, reference.getCollectionIdentifier(), reference.getSubjectIdentifier());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to the permission service
|
||||||
|
*/
|
||||||
private final LPPermissionService service;
|
private final LPPermissionService service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of the collection which holds the subject
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
|
@Nonnull
|
||||||
private final String collectionIdentifier;
|
private final String collectionIdentifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of the subject
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
|
@Nonnull
|
||||||
private final String subjectIdentifier;
|
private final String subjectIdentifier;
|
||||||
|
|
||||||
|
// cache
|
||||||
private long lastLookup = 0L;
|
private long lastLookup = 0L;
|
||||||
private WeakReference<LPSubject> cache = null;
|
private WeakReference<LPSubject> cache = null;
|
||||||
|
|
||||||
private synchronized LPSubject resolveDirectly() {
|
SubjectReference(LPPermissionService service, String collectionIdentifier, String subjectIdentifier) {
|
||||||
long sinceLast = System.currentTimeMillis() - lastLookup;
|
this.service = Preconditions.checkNotNull(service);
|
||||||
|
this.collectionIdentifier = Preconditions.checkNotNull(collectionIdentifier);
|
||||||
|
this.subjectIdentifier = Preconditions.checkNotNull(subjectIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
// try the cache
|
private LPSubject tryCache() {
|
||||||
if (sinceLast < TimeUnit.SECONDS.toMillis(10)) {
|
if ((System.currentTimeMillis() - lastLookup) < CACHE_TIME) {
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
LPSubject s = cache.get();
|
return cache.get();
|
||||||
if (s != null) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LPSubject s = service.getCollection(collectionIdentifier).loadSubject(subjectIdentifier).join();
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized LPSubject resolveDirectly() {
|
||||||
|
/* As this method is synchronized, it's possible that since this was invoked
|
||||||
|
the subject has been cached.
|
||||||
|
Therefore, we check the cache again, and return if there's a value present.
|
||||||
|
This effectively means all calls to this method block, but all return the same value
|
||||||
|
at the same time once the data is loaded :) */
|
||||||
|
|
||||||
|
LPSubject s = tryCache();
|
||||||
|
if (s != null) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// subject isn't cached, so make a call to load it
|
||||||
|
s = service.getCollection(collectionIdentifier).loadSubject(subjectIdentifier).join();
|
||||||
|
|
||||||
|
// cache the result
|
||||||
lastLookup = System.currentTimeMillis();
|
lastLookup = System.currentTimeMillis();
|
||||||
cache = new WeakReference<>(s);
|
cache = new WeakReference<>(s);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<LPSubject> resolveLp() {
|
public CompletableFuture<LPSubject> resolveLp() {
|
||||||
long sinceLast = System.currentTimeMillis() - lastLookup;
|
// check if there is a cached value before loading
|
||||||
|
LPSubject s = tryCache();
|
||||||
// try the cache
|
if (s != null) {
|
||||||
if (sinceLast < TimeUnit.SECONDS.toMillis(10)) {
|
return CompletableFuture.completedFuture(s);
|
||||||
if (cache != null) {
|
|
||||||
LPSubject s = cache.get();
|
|
||||||
if (s != null) {
|
|
||||||
return CompletableFuture.completedFuture(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load the subject
|
||||||
return CompletableFuture.supplyAsync(this::resolveDirectly);
|
return CompletableFuture.supplyAsync(this::resolveDirectly);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Subject> resolve() {
|
public CompletableFuture<Subject> resolve() {
|
||||||
long sinceLast = System.currentTimeMillis() - lastLookup;
|
// check if there is a cached value before loading
|
||||||
|
LPSubject s = tryCache();
|
||||||
// try the cache
|
if (s != null) {
|
||||||
if (sinceLast < TimeUnit.SECONDS.toMillis(10)) {
|
return CompletableFuture.completedFuture(s.sponge());
|
||||||
if (cache != null) {
|
|
||||||
LPSubject s = cache.get();
|
|
||||||
if (s != null) {
|
|
||||||
return CompletableFuture.completedFuture(s.sponge());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load the subject
|
||||||
return CompletableFuture.supplyAsync(() -> resolveDirectly().sponge());
|
return CompletableFuture.supplyAsync(() -> resolveDirectly().sponge());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) return true;
|
||||||
|
if (!(o instanceof SubjectReference)) return false;
|
||||||
|
final SubjectReference other = (SubjectReference) o;
|
||||||
|
return this.collectionIdentifier.equals(other.collectionIdentifier) && this.subjectIdentifier.equals(other.subjectIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int PRIME = 59;
|
||||||
|
int result = 1;
|
||||||
|
result = result * PRIME + this.collectionIdentifier.hashCode();
|
||||||
|
result = result * PRIME + this.subjectIdentifier.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SubjectReference(" +
|
||||||
|
"collectionIdentifier=" + this.collectionIdentifier + ", " +
|
||||||
|
"subjectIdentifier=" + this.subjectIdentifier + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of LuckPerms, licensed under the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* 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.sponge.service.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
|
||||||
|
import org.spongepowered.api.service.permission.Subject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches the creation of {@link SubjectReference}s.
|
||||||
|
*/
|
||||||
|
@UtilityClass
|
||||||
|
public final class SubjectReferenceFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache based factory for SubjectReferences.
|
||||||
|
*
|
||||||
|
* Using a factory and caching here makes the Subject cache in SubjectReference
|
||||||
|
* more effective. This reduces the no. of times i/o is executed due to resolve calls
|
||||||
|
* within the SubjectReference.
|
||||||
|
*
|
||||||
|
* It's perfectly ok if two instances of the same SubjectReference exist. (hence the 1 hour expiry)
|
||||||
|
*/
|
||||||
|
private static final LoadingCache<SubjectReferenceAttributes, SubjectReference> REFERENCE_CACHE = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(1, TimeUnit.HOURS)
|
||||||
|
.build(a -> new SubjectReference(a.permissionService, a.collectionId, a.id));
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static SubjectReference deserialize(@NonNull LPPermissionService service, @NonNull String serialisedReference) {
|
||||||
|
List<String> parts = Splitter.on('/').limit(2).splitToList(serialisedReference);
|
||||||
|
return obtain(service, parts.get(0), parts.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SubjectReference obtain(@NonNull LPPermissionService service, @NonNull Subject subject) {
|
||||||
|
return obtain(service, subject.getContainingCollection().getIdentifier(), subject.getIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SubjectReference obtain(@NonNull LPPermissionService service, @NonNull org.spongepowered.api.service.permission.SubjectReference reference) {
|
||||||
|
if (reference instanceof SubjectReference) {
|
||||||
|
return ((SubjectReference) reference);
|
||||||
|
} else {
|
||||||
|
return SubjectReferenceFactory.obtain(service, reference.getCollectionIdentifier(), reference.getSubjectIdentifier());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SubjectReference obtain(@NonNull LPPermissionService service, @NonNull String collectionIdentifier, @NonNull String subjectIdentifier) {
|
||||||
|
return REFERENCE_CACHE.get(new SubjectReferenceAttributes(service, collectionIdentifier, subjectIdentifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used as a cache key.
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
private static final class SubjectReferenceAttributes {
|
||||||
|
private final LPPermissionService permissionService;
|
||||||
|
|
||||||
|
private final String collectionId;
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) return true;
|
||||||
|
if (!(o instanceof SubjectReferenceAttributes)) return false;
|
||||||
|
final SubjectReferenceAttributes other = (SubjectReferenceAttributes) o;
|
||||||
|
return this.collectionId.equals(other.collectionId) && this.id.equals(other.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int PRIME = 59;
|
||||||
|
int result = 1;
|
||||||
|
result = result * PRIME + this.collectionId.hashCode();
|
||||||
|
result = result * PRIME + this.id.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -81,7 +81,6 @@ import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
|||||||
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
|
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
|
||||||
import me.lucko.luckperms.sponge.service.persisted.PersistedCollection;
|
import me.lucko.luckperms.sponge.service.persisted.PersistedCollection;
|
||||||
import me.lucko.luckperms.sponge.tasks.ServiceCacheHousekeepingTask;
|
import me.lucko.luckperms.sponge.tasks.ServiceCacheHousekeepingTask;
|
||||||
import me.lucko.luckperms.sponge.timings.LPTimings;
|
|
||||||
import me.lucko.luckperms.sponge.utils.VersionData;
|
import me.lucko.luckperms.sponge.utils.VersionData;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -122,8 +121,18 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LuckPerms implementation for the Sponge API.
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Plugin(id = "luckperms", name = "LuckPerms", version = VersionData.VERSION, authors = {"Luck"}, description = "A permissions plugin")
|
@Plugin(
|
||||||
|
id = "luckperms",
|
||||||
|
name = "LuckPerms",
|
||||||
|
version = VersionData.VERSION,
|
||||||
|
authors = {"Luck"},
|
||||||
|
description = "A permissions plugin",
|
||||||
|
url = "https://github.com/lucko/LuckPerms"
|
||||||
|
)
|
||||||
public class LPSpongePlugin implements LuckPermsPlugin {
|
public class LPSpongePlugin implements LuckPermsPlugin {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -146,7 +155,6 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
|||||||
@AsynchronousExecutor
|
@AsynchronousExecutor
|
||||||
private SpongeExecutorService asyncExecutorService;
|
private SpongeExecutorService asyncExecutorService;
|
||||||
|
|
||||||
private LPTimings timings;
|
|
||||||
private boolean lateLoad = false;
|
private boolean lateLoad = false;
|
||||||
private long startTime;
|
private long startTime;
|
||||||
|
|
||||||
@ -186,7 +194,6 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
|||||||
verboseHandler = new VerboseHandler(scheduler.async(), getVersion());
|
verboseHandler = new VerboseHandler(scheduler.async(), getVersion());
|
||||||
permissionVault = new PermissionVault(scheduler.async());
|
permissionVault = new PermissionVault(scheduler.async());
|
||||||
logDispatcher = new LogDispatcher(this);
|
logDispatcher = new LogDispatcher(this);
|
||||||
timings = new LPTimings(this);
|
|
||||||
|
|
||||||
getLog().info("Loading configuration...");
|
getLog().info("Loading configuration...");
|
||||||
configuration = new SpongeConfig(this);
|
configuration = new SpongeConfig(this);
|
||||||
|
@ -29,7 +29,6 @@ import com.google.common.base.Splitter;
|
|||||||
|
|
||||||
import me.lucko.luckperms.common.commands.CommandManager;
|
import me.lucko.luckperms.common.commands.CommandManager;
|
||||||
import me.lucko.luckperms.common.commands.utils.Util;
|
import me.lucko.luckperms.common.commands.utils.Util;
|
||||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
|
||||||
|
|
||||||
import org.spongepowered.api.command.CommandCallable;
|
import org.spongepowered.api.command.CommandCallable;
|
||||||
import org.spongepowered.api.command.CommandException;
|
import org.spongepowered.api.command.CommandException;
|
||||||
@ -41,16 +40,13 @@ import org.spongepowered.api.text.selector.Selector;
|
|||||||
import org.spongepowered.api.world.Location;
|
import org.spongepowered.api.world.Location;
|
||||||
import org.spongepowered.api.world.World;
|
import org.spongepowered.api.world.World;
|
||||||
|
|
||||||
import co.aikar.timings.Timing;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@SuppressWarnings("NullableProblems")
|
public class SpongeCommand extends CommandManager implements CommandCallable {
|
||||||
class SpongeCommand extends CommandManager implements CommandCallable {
|
|
||||||
private final LPSpongePlugin plugin;
|
private final LPSpongePlugin plugin;
|
||||||
|
|
||||||
SpongeCommand(LPSpongePlugin plugin) {
|
SpongeCommand(LPSpongePlugin plugin) {
|
||||||
@ -58,56 +54,56 @@ class SpongeCommand extends CommandManager implements CommandCallable {
|
|||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private List<String> processArgs(CommandSource source, String s) {
|
||||||
public CommandResult process(CommandSource source, String s) throws CommandException {
|
List<String> args = Util.stripQuotes(Splitter.on(COMMAND_SEPARATOR_PATTERN).omitEmptyStrings().splitToList(s));
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.ON_COMMAND)) {
|
|
||||||
List<String> args = Util.stripQuotes(Splitter.on(COMMAND_SEPARATOR_PATTERN).omitEmptyStrings().splitToList(s));
|
|
||||||
|
|
||||||
// resolve selectors
|
// resolve selectors
|
||||||
ListIterator<String> it = args.listIterator();
|
ListIterator<String> it = args.listIterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
String element = it.next();
|
String element = it.next();
|
||||||
if (element.startsWith("@")) {
|
if (element.startsWith("@")) {
|
||||||
try {
|
try {
|
||||||
Player ret = Selector.parse(element).resolve(source).stream()
|
Player ret = Selector.parse(element).resolve(source).stream()
|
||||||
.filter(e -> e instanceof Player)
|
.filter(e -> e instanceof Player)
|
||||||
.map(e -> ((Player) e))
|
.map(e -> ((Player) e))
|
||||||
.findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
|
|
||||||
if (ret != null) {
|
if (ret != null) {
|
||||||
it.set(ret.getUniqueId().toString());
|
it.set(ret.getUniqueId().toString());
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// ignored
|
|
||||||
}
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCommand(plugin.getSenderFactory().wrap(source), "lp", args);
|
|
||||||
return CommandResult.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandResult process(CommandSource source, String s) throws CommandException {
|
||||||
|
onCommand(plugin.getSenderFactory().wrap(source), "lp", processArgs(source, s));
|
||||||
|
return CommandResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getSuggestions(CommandSource source, String s, @Nullable Location<World> location) throws CommandException {
|
public List<String> getSuggestions(CommandSource source, String s, @Nullable Location<World> location) throws CommandException {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.COMMAND_TAB_COMPLETE)) {
|
return onTabComplete(plugin.getSenderFactory().wrap(source), processArgs(source, s));
|
||||||
return onTabComplete(plugin.getSenderFactory().wrap(source), Splitter.on(' ').splitToList(s));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean testPermission(CommandSource source) {
|
public boolean testPermission(CommandSource source) {
|
||||||
return true;
|
return true; // we run permission checks internally
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Text> getShortDescription(CommandSource source) {
|
public Optional<Text> getShortDescription(CommandSource source) {
|
||||||
return Optional.of(Text.of("LuckPerms main command."));
|
return Optional.of(Text.of("Manage permissions"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Text> getHelp(CommandSource source) {
|
public Optional<Text> getHelp(CommandSource source) {
|
||||||
return Optional.of(Text.of("Type /luckperms for help."));
|
return Optional.of(Text.of("Run /luckperms to view usage."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -34,7 +34,6 @@ import me.lucko.luckperms.common.locale.Message;
|
|||||||
import me.lucko.luckperms.common.model.User;
|
import me.lucko.luckperms.common.model.User;
|
||||||
import me.lucko.luckperms.common.utils.LoginHelper;
|
import me.lucko.luckperms.common.utils.LoginHelper;
|
||||||
import me.lucko.luckperms.common.utils.UuidCache;
|
import me.lucko.luckperms.common.utils.UuidCache;
|
||||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
|
||||||
|
|
||||||
import org.spongepowered.api.command.CommandSource;
|
import org.spongepowered.api.command.CommandSource;
|
||||||
import org.spongepowered.api.entity.living.player.Player;
|
import org.spongepowered.api.entity.living.player.Player;
|
||||||
@ -48,8 +47,6 @@ import org.spongepowered.api.text.serializer.TextSerializers;
|
|||||||
import org.spongepowered.api.util.Tristate;
|
import org.spongepowered.api.util.Tristate;
|
||||||
import org.spongepowered.api.world.World;
|
import org.spongepowered.api.world.World;
|
||||||
|
|
||||||
import co.aikar.timings.Timing;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -142,52 +139,50 @@ public class SpongeListener {
|
|||||||
@Listener(order = Order.FIRST)
|
@Listener(order = Order.FIRST)
|
||||||
@IsCancelled(Tristate.UNDEFINED)
|
@IsCancelled(Tristate.UNDEFINED)
|
||||||
public void onClientLogin(ClientConnectionEvent.Login e) {
|
public void onClientLogin(ClientConnectionEvent.Login e) {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.ON_CLIENT_LOGIN)) {
|
/* Called when the player starts logging into the server.
|
||||||
/* Called when the player starts logging into the server.
|
|
||||||
At this point, the users data should be present and loaded.
|
At this point, the users data should be present and loaded.
|
||||||
Listening on LOW priority to allow plugins to further modify data here. (auth plugins, etc.) */
|
Listening on LOW priority to allow plugins to further modify data here. (auth plugins, etc.) */
|
||||||
|
|
||||||
final GameProfile player = e.getProfile();
|
final GameProfile player = e.getProfile();
|
||||||
|
|
||||||
if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
||||||
plugin.getLog().info("Processing login event for " + player.getUniqueId() + " - " + player.getName());
|
plugin.getLog().info("Processing login event for " + player.getUniqueId() + " - " + player.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
|
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
|
||||||
|
|
||||||
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
|
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
deniedLogin.add(player.getUniqueId());
|
deniedLogin.add(player.getUniqueId());
|
||||||
|
|
||||||
plugin.getLog().warn("User " + player.getUniqueId() + " - " + player.getName() + " doesn't have data pre-loaded. - denying login.");
|
plugin.getLog().warn("User " + player.getUniqueId() + " - " + player.getName() + " doesn't have data pre-loaded. - denying login.");
|
||||||
e.setCancelled(true);
|
e.setCancelled(true);
|
||||||
e.setMessageCancelled(false);
|
e.setMessageCancelled(false);
|
||||||
//noinspection deprecation
|
//noinspection deprecation
|
||||||
e.setMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(Message.LOADING_ERROR.asString(plugin.getLocaleManager())));
|
e.setMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(Message.LOADING_ERROR.asString(plugin.getLocaleManager())));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to pre-process some permissions for the user to save time later. Might not work, but it's better than nothing.
|
// Attempt to pre-process some permissions for the user to save time later. Might not work, but it's better than nothing.
|
||||||
Optional<Player> p = e.getCause().first(Player.class);
|
Optional<Player> p = e.getCause().first(Player.class);
|
||||||
if (p.isPresent()) {
|
if (p.isPresent()) {
|
||||||
MutableContextSet context = MutableContextSet.fromSet(plugin.getContextManager().getApplicableContext(p.get()));
|
MutableContextSet context = MutableContextSet.fromSet(plugin.getContextManager().getApplicableContext(p.get()));
|
||||||
|
|
||||||
List<String> worlds = plugin.getGame().isServerAvailable() ? plugin.getGame().getServer().getWorlds().stream()
|
List<String> worlds = plugin.getGame().isServerAvailable() ? plugin.getGame().getServer().getWorlds().stream()
|
||||||
.map(World::getName)
|
.map(World::getName)
|
||||||
.collect(Collectors.toList()) : Collections.emptyList();
|
.collect(Collectors.toList()) : Collections.emptyList();
|
||||||
|
|
||||||
plugin.doAsync(() -> {
|
plugin.doAsync(() -> {
|
||||||
UserData data = user.getUserData();
|
UserData data = user.getUserData();
|
||||||
data.preCalculate(plugin.getService().calculateContexts(context.makeImmutable()));
|
data.preCalculate(plugin.getService().calculateContexts(context.makeImmutable()));
|
||||||
|
|
||||||
for (String world : worlds) {
|
for (String world : worlds) {
|
||||||
MutableContextSet modified = MutableContextSet.fromSet(context);
|
MutableContextSet modified = MutableContextSet.fromSet(context);
|
||||||
modified.removeAll("world");
|
modified.removeAll("world");
|
||||||
modified.add("world", world);
|
modified.add("world", world);
|
||||||
data.preCalculate(plugin.getService().calculateContexts(modified.makeImmutable()));
|
data.preCalculate(plugin.getService().calculateContexts(modified.makeImmutable()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,12 +213,11 @@ public class SpongeListener {
|
|||||||
/* We don't actually remove the user instance here, as Sponge likes to keep performing checks
|
/* We don't actually remove the user instance here, as Sponge likes to keep performing checks
|
||||||
on players when they disconnect. The instance gets cleared up on a housekeeping task
|
on players when they disconnect. The instance gets cleared up on a housekeeping task
|
||||||
after a period of inactivity. */
|
after a period of inactivity. */
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.ON_CLIENT_LEAVE)) {
|
|
||||||
final UuidCache cache = plugin.getUuidCache();
|
|
||||||
|
|
||||||
// Unload the user from memory when they disconnect
|
final UuidCache cache = plugin.getUuidCache();
|
||||||
cache.clearCache(e.getTargetEntity().getUniqueId());
|
|
||||||
}
|
// Unload the user from memory when they disconnect
|
||||||
|
cache.clearCache(e.getTargetEntity().getUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener
|
@Listener
|
||||||
|
@ -60,7 +60,7 @@ public class SpongeCalculatorLink implements ContextCalculator<Subject> {
|
|||||||
delegate.accumulateContexts(subject, contexts);
|
delegate.accumulateContexts(subject, contexts);
|
||||||
accumulator.addAll(CompatibilityUtil.convertContexts(contexts));
|
accumulator.addAll(CompatibilityUtil.convertContexts(contexts));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
new RuntimeException("Exception thrown by delegate Sponge calculator: " + delegate.getClass().getName(), e).printStackTrace();
|
throw new RuntimeException("Exception thrown by delegate Sponge calculator: " + delegate.getClass().getName(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return accumulator;
|
return accumulator;
|
||||||
|
@ -47,13 +47,13 @@ import java.util.Set;
|
|||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class SpongeMigrationUtils {
|
public class SpongeMigrationUtils {
|
||||||
|
|
||||||
public static void migrateSubject(Subject subject, PermissionHolder holder, int priority) {
|
public static void migrateSubject(Subject from, PermissionHolder to, int priority) {
|
||||||
if (holder instanceof Group) {
|
if (to instanceof Group) {
|
||||||
MigrationUtils.setGroupWeight((Group) holder, priority);
|
MigrationUtils.setGroupWeight((Group) to, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate permissions
|
// Migrate permissions
|
||||||
Map<Set<Context>, Map<String, Boolean>> perms = subject.getSubjectData().getAllPermissions();
|
Map<Set<Context>, Map<String, Boolean>> perms = from.getSubjectData().getAllPermissions();
|
||||||
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : perms.entrySet()) {
|
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : perms.entrySet()) {
|
||||||
ContextSet context = CompatibilityUtil.convertContexts(e.getKey());
|
ContextSet context = CompatibilityUtil.convertContexts(e.getKey());
|
||||||
|
|
||||||
@ -62,12 +62,12 @@ public class SpongeMigrationUtils {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.setPermission(NodeFactory.newBuilder(perm.getKey()).withExtraContext(context).setValue(perm.getValue()).build());
|
to.setPermission(NodeFactory.newBuilder(perm.getKey()).withExtraContext(context).setValue(perm.getValue()).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate options
|
// Migrate options
|
||||||
Map<Set<Context>, Map<String, String>> opts = subject.getSubjectData().getAllOptions();
|
Map<Set<Context>, Map<String, String>> opts = from.getSubjectData().getAllOptions();
|
||||||
for (Map.Entry<Set<Context>, Map<String, String>> e : opts.entrySet()) {
|
for (Map.Entry<Set<Context>, Map<String, String>> e : opts.entrySet()) {
|
||||||
ContextSet context = CompatibilityUtil.convertContexts(e.getKey());
|
ContextSet context = CompatibilityUtil.convertContexts(e.getKey());
|
||||||
|
|
||||||
@ -77,17 +77,17 @@ public class SpongeMigrationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opt.getKey().equalsIgnoreCase("prefix")) {
|
if (opt.getKey().equalsIgnoreCase("prefix")) {
|
||||||
holder.setPermission(NodeFactory.makePrefixNode(priority, opt.getValue()).withExtraContext(context).setValue(true).build());
|
to.setPermission(NodeFactory.makePrefixNode(priority, opt.getValue()).withExtraContext(context).setValue(true).build());
|
||||||
} else if (opt.getKey().equalsIgnoreCase("suffix")) {
|
} else if (opt.getKey().equalsIgnoreCase("suffix")) {
|
||||||
holder.setPermission(NodeFactory.makeSuffixNode(priority, opt.getValue()).withExtraContext(context).setValue(true).build());
|
to.setPermission(NodeFactory.makeSuffixNode(priority, opt.getValue()).withExtraContext(context).setValue(true).build());
|
||||||
} else {
|
} else {
|
||||||
holder.setPermission(NodeFactory.makeMetaNode(opt.getKey(), opt.getValue()).withExtraContext(context).setValue(true).build());
|
to.setPermission(NodeFactory.makeMetaNode(opt.getKey(), opt.getValue()).withExtraContext(context).setValue(true).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate parents
|
// Migrate parents
|
||||||
Map<Set<Context>, List<Subject>> parents = subject.getSubjectData().getAllParents();
|
Map<Set<Context>, List<Subject>> parents = from.getSubjectData().getAllParents();
|
||||||
for (Map.Entry<Set<Context>, List<Subject>> e : parents.entrySet()) {
|
for (Map.Entry<Set<Context>, List<Subject>> e : parents.entrySet()) {
|
||||||
ContextSet context = CompatibilityUtil.convertContexts(e.getKey());
|
ContextSet context = CompatibilityUtil.convertContexts(e.getKey());
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ public class SpongeMigrationUtils {
|
|||||||
continue; // LuckPerms does not support persisting other subject types.
|
continue; // LuckPerms does not support persisting other subject types.
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.setPermission(NodeFactory.newBuilder("group." + MigrationUtils.standardizeName(s.getIdentifier())).withExtraContext(context).setValue(true).build());
|
to.setPermission(NodeFactory.newBuilder("group." + MigrationUtils.standardizeName(s.getIdentifier())).withExtraContext(context).setValue(true).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,15 +48,12 @@ import me.lucko.luckperms.sponge.service.model.CompatibilityUtil;
|
|||||||
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
||||||
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
|
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
|
||||||
|
|
||||||
import org.spongepowered.api.command.CommandSource;
|
import org.spongepowered.api.command.CommandSource;
|
||||||
import org.spongepowered.api.service.permission.NodeTree;
|
import org.spongepowered.api.service.permission.NodeTree;
|
||||||
import org.spongepowered.api.service.permission.PermissionService;
|
import org.spongepowered.api.service.permission.PermissionService;
|
||||||
import org.spongepowered.api.service.permission.Subject;
|
import org.spongepowered.api.service.permission.Subject;
|
||||||
|
|
||||||
import co.aikar.timings.Timing;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -176,69 +173,59 @@ public class SpongeGroup extends Group {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
|
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PERMISSION_VALUE)) {
|
NodeTree nt = permissionCache.get(contexts);
|
||||||
NodeTree nt = permissionCache.get(contexts);
|
Tristate t = CompatibilityUtil.convertTristate(nt.get(permission));
|
||||||
Tristate t = CompatibilityUtil.convertTristate(nt.get(permission));
|
if (t != Tristate.UNDEFINED) {
|
||||||
if (t != Tristate.UNDEFINED) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
t = plugin.getService().getGroupSubjects().getDefaults().getPermissionValue(contexts, permission);
|
|
||||||
if (t != Tristate.UNDEFINED) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
t = plugin.getService().getDefaults().getPermissionValue(contexts, permission);
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t = plugin.getService().getGroupSubjects().getDefaults().getPermissionValue(contexts, permission);
|
||||||
|
if (t != Tristate.UNDEFINED) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = plugin.getService().getDefaults().getPermissionValue(contexts, permission);
|
||||||
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isChildOf(ImmutableContextSet contexts, SubjectReference parent) {
|
public boolean isChildOf(ImmutableContextSet contexts, SubjectReference parent) {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_IS_CHILD_OF)) {
|
return parent.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, "group." + parent.getSubjectIdentifier()).asBoolean();
|
||||||
return parent.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, "group." + parent.getSubjectIdentifier()).asBoolean();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<SubjectReference> getParents(ImmutableContextSet contexts) {
|
public ImmutableList<SubjectReference> getParents(ImmutableContextSet contexts) {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PARENTS)) {
|
return parentCache.get(contexts);
|
||||||
return parentCache.get(contexts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getOption(ImmutableContextSet contexts, String s) {
|
public Optional<String> getOption(ImmutableContextSet contexts, String s) {
|
||||||
try (Timing ignored = plugin.getService().getPlugin().getTimings().time(LPTiming.GROUP_GET_OPTION)) {
|
Optional<String> option;
|
||||||
Optional<String> option;
|
if (s.equalsIgnoreCase("prefix")) {
|
||||||
if (s.equalsIgnoreCase("prefix")) {
|
option = getChatMeta(contexts, ChatMetaType.PREFIX);
|
||||||
option = getChatMeta(contexts, ChatMetaType.PREFIX);
|
|
||||||
|
|
||||||
} else if (s.equalsIgnoreCase("suffix")) {
|
} else if (s.equalsIgnoreCase("suffix")) {
|
||||||
option = getChatMeta(contexts, ChatMetaType.SUFFIX);
|
option = getChatMeta(contexts, ChatMetaType.SUFFIX);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
option = getMeta(contexts, s);
|
option = getMeta(contexts, s);
|
||||||
}
|
|
||||||
|
|
||||||
if (option.isPresent()) {
|
|
||||||
return option;
|
|
||||||
}
|
|
||||||
|
|
||||||
option = plugin.getService().getGroupSubjects().getDefaults().getOption(contexts, s);
|
|
||||||
if (option.isPresent()) {
|
|
||||||
return option;
|
|
||||||
}
|
|
||||||
|
|
||||||
return plugin.getService().getDefaults().getOption(contexts, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (option.isPresent()) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
|
option = plugin.getService().getGroupSubjects().getDefaults().getOption(contexts, s);
|
||||||
|
if (option.isPresent()) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin.getService().getDefaults().getOption(contexts, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableContextSet getActiveContextSet() {
|
public ImmutableContextSet getActiveContextSet() {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_ACTIVE_CONTEXTS)) {
|
return plugin.getContextManager().getApplicableContext(this.sponge()).makeImmutable();
|
||||||
return plugin.getContextManager().getApplicableContext(this.sponge()).makeImmutable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<String> getChatMeta(ImmutableContextSet contexts, ChatMetaType type) {
|
private Optional<String> getChatMeta(ImmutableContextSet contexts, ChatMetaType type) {
|
||||||
|
@ -42,7 +42,6 @@ import me.lucko.luckperms.sponge.service.ProxyFactory;
|
|||||||
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
||||||
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
|
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
|
||||||
|
|
||||||
import org.spongepowered.api.Sponge;
|
import org.spongepowered.api.Sponge;
|
||||||
import org.spongepowered.api.command.CommandSource;
|
import org.spongepowered.api.command.CommandSource;
|
||||||
@ -50,8 +49,6 @@ import org.spongepowered.api.entity.living.player.Player;
|
|||||||
import org.spongepowered.api.service.permission.PermissionService;
|
import org.spongepowered.api.service.permission.PermissionService;
|
||||||
import org.spongepowered.api.service.permission.Subject;
|
import org.spongepowered.api.service.permission.Subject;
|
||||||
|
|
||||||
import co.aikar.timings.Timing;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -129,75 +126,66 @@ public class SpongeUser extends User {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
|
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PERMISSION_VALUE)) {
|
return parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
|
||||||
return parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isChildOf(ImmutableContextSet contexts, SubjectReference parent) {
|
public boolean isChildOf(ImmutableContextSet contexts, SubjectReference parent) {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_IS_CHILD_OF)) {
|
return parent.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, "group." + parent.getSubjectIdentifier()).asBoolean();
|
||||||
return parent.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, "group." + parent.getSubjectIdentifier()).asBoolean();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<SubjectReference> getParents(ImmutableContextSet contexts) {
|
public ImmutableList<SubjectReference> getParents(ImmutableContextSet contexts) {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PARENTS)) {
|
ImmutableSet.Builder<SubjectReference> subjects = ImmutableSet.builder();
|
||||||
ImmutableSet.Builder<SubjectReference> subjects = ImmutableSet.builder();
|
|
||||||
|
|
||||||
for (String perm : parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getImmutableBacking().keySet()) {
|
for (String perm : parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getImmutableBacking().keySet()) {
|
||||||
if (!perm.startsWith("group.")) {
|
if (!perm.startsWith("group.")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
String groupName = perm.substring("group.".length());
|
|
||||||
if (plugin.getGroupManager().isLoaded(groupName)) {
|
|
||||||
subjects.add(plugin.getService().getGroupSubjects().loadSubject(groupName).join().toReference());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subjects.addAll(plugin.getService().getUserSubjects().getDefaults().getParents(contexts));
|
String groupName = perm.substring("group.".length());
|
||||||
subjects.addAll(plugin.getService().getDefaults().getParents(contexts));
|
if (plugin.getGroupManager().isLoaded(groupName)) {
|
||||||
|
subjects.add(plugin.getService().getGroupSubjects().loadSubject(groupName).join().toReference());
|
||||||
return getService().sortSubjects(subjects.build());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subjects.addAll(plugin.getService().getUserSubjects().getDefaults().getParents(contexts));
|
||||||
|
subjects.addAll(plugin.getService().getDefaults().getParents(contexts));
|
||||||
|
|
||||||
|
return getService().sortSubjects(subjects.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getOption(ImmutableContextSet contexts, String s) {
|
public Optional<String> getOption(ImmutableContextSet contexts, String s) {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_OPTION)) {
|
MetaData data = parent.getUserData().getMetaData(plugin.getService().calculateContexts(contexts));
|
||||||
MetaData data = parent.getUserData().getMetaData(plugin.getService().calculateContexts(contexts));
|
if (s.equalsIgnoreCase("prefix")) {
|
||||||
if (s.equalsIgnoreCase("prefix")) {
|
if (data.getPrefix() != null) {
|
||||||
if (data.getPrefix() != null) {
|
return Optional.of(data.getPrefix());
|
||||||
return Optional.of(data.getPrefix());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.equalsIgnoreCase("suffix")) {
|
|
||||||
if (data.getSuffix() != null) {
|
|
||||||
return Optional.of(data.getSuffix());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.getMeta().containsKey(s)) {
|
|
||||||
return Optional.of(data.getMeta().get(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<String> v = plugin.getService().getUserSubjects().getDefaults().getOption(contexts, s);
|
|
||||||
if (v.isPresent()) {
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
return plugin.getService().getDefaults().getOption(contexts, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s.equalsIgnoreCase("suffix")) {
|
||||||
|
if (data.getSuffix() != null) {
|
||||||
|
return Optional.of(data.getSuffix());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String val = data.getMeta().get(s);
|
||||||
|
if (val != null) {
|
||||||
|
return Optional.of(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<String> v = plugin.getService().getUserSubjects().getDefaults().getOption(contexts, s);
|
||||||
|
if (v.isPresent()) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin.getService().getDefaults().getOption(contexts, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableContextSet getActiveContextSet() {
|
public ImmutableContextSet getActiveContextSet() {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_ACTIVE_CONTEXTS)) {
|
return plugin.getContextManager().getApplicableContext(this.sponge());
|
||||||
return plugin.getContextManager().getApplicableContext(this.sponge());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ import lombok.AccessLevel;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@ -58,17 +57,15 @@ import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
|||||||
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
||||||
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
|
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||||
|
import me.lucko.luckperms.sponge.service.model.SubjectReferenceFactory;
|
||||||
import me.lucko.luckperms.sponge.service.persisted.PersistedCollection;
|
import me.lucko.luckperms.sponge.service.persisted.PersistedCollection;
|
||||||
import me.lucko.luckperms.sponge.service.storage.SubjectStorage;
|
import me.lucko.luckperms.sponge.service.storage.SubjectStorage;
|
||||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
|
||||||
|
|
||||||
import org.spongepowered.api.plugin.PluginContainer;
|
import org.spongepowered.api.plugin.PluginContainer;
|
||||||
import org.spongepowered.api.service.permission.PermissionService;
|
import org.spongepowered.api.service.permission.PermissionService;
|
||||||
import org.spongepowered.api.service.permission.Subject;
|
import org.spongepowered.api.service.permission.Subject;
|
||||||
import org.spongepowered.api.text.Text;
|
import org.spongepowered.api.text.Text;
|
||||||
|
|
||||||
import co.aikar.timings.Timing;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -81,7 +78,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The LuckPerms implementation of the Sponge Permission Service
|
* LuckPerms implementation of the Sponge Permission Service
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public class LuckPermsService implements LPPermissionService {
|
public class LuckPermsService implements LPPermissionService {
|
||||||
@ -105,17 +102,7 @@ public class LuckPermsService implements LPPermissionService {
|
|||||||
|
|
||||||
@Getter(value = AccessLevel.NONE)
|
@Getter(value = AccessLevel.NONE)
|
||||||
private final LoadingCache<String, LPSubjectCollection> collections = Caffeine.newBuilder()
|
private final LoadingCache<String, LPSubjectCollection> collections = Caffeine.newBuilder()
|
||||||
.build(new CacheLoader<String, LPSubjectCollection>() {
|
.build(s -> new PersistedCollection(this, s));
|
||||||
@Override
|
|
||||||
public LPSubjectCollection load(String s) {
|
|
||||||
return new PersistedCollection(LuckPermsService.this, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LPSubjectCollection reload(String s, LPSubjectCollection collection) {
|
|
||||||
return collection; // Never needs to be refreshed.
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
public LuckPermsService(LPSpongePlugin plugin) {
|
public LuckPermsService(LPSpongePlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
@ -134,8 +121,8 @@ public class LuckPermsService implements LPPermissionService {
|
|||||||
defaultSubjects = new PersistedCollection(this, "defaults");
|
defaultSubjects = new PersistedCollection(this, "defaults");
|
||||||
defaultSubjects.loadAll();
|
defaultSubjects.loadAll();
|
||||||
|
|
||||||
collections.put(PermissionService.SUBJECTS_USER, userSubjects);
|
collections.put("user", userSubjects);
|
||||||
collections.put(PermissionService.SUBJECTS_GROUP, groupSubjects);
|
collections.put("group", groupSubjects);
|
||||||
collections.put("defaults", defaultSubjects);
|
collections.put("defaults", defaultSubjects);
|
||||||
|
|
||||||
for (String collection : storage.getSavedCollections()) {
|
for (String collection : storage.getSavedCollections()) {
|
||||||
@ -168,9 +155,7 @@ public class LuckPermsService implements LPPermissionService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LPSubjectCollection getCollection(String s) {
|
public LPSubjectCollection getCollection(String s) {
|
||||||
try (Timing ignored = plugin.getTimings().time(LPTiming.GET_SUBJECTS)) {
|
return collections.get(s.toLowerCase());
|
||||||
return collections.get(s.toLowerCase());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -180,7 +165,7 @@ public class LuckPermsService implements LPPermissionService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubjectReference newSubjectReference(String collectionIdentifier, String subjectIdentifier) {
|
public SubjectReference newSubjectReference(String collectionIdentifier, String subjectIdentifier) {
|
||||||
return SubjectReference.of(this, collectionIdentifier, subjectIdentifier);
|
return SubjectReferenceFactory.obtain(this, collectionIdentifier, subjectIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,12 +46,9 @@ import me.lucko.luckperms.common.node.NodeFactory;
|
|||||||
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
||||||
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
|
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
|
||||||
|
|
||||||
import org.spongepowered.api.service.permission.PermissionService;
|
import org.spongepowered.api.service.permission.PermissionService;
|
||||||
|
|
||||||
import co.aikar.timings.Timing;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -75,388 +72,358 @@ public class LuckPermsSubjectData implements LPSubjectData {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions() {
|
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions() {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) {
|
Map<ImmutableContextSet, ImmutableMap.Builder<String, Boolean>> perms = new HashMap<>();
|
||||||
Map<ImmutableContextSet, ImmutableMap.Builder<String, Boolean>> perms = new HashMap<>();
|
|
||||||
|
|
||||||
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) {
|
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) {
|
||||||
ImmutableMap.Builder<String, Boolean> results = ImmutableMap.builder();
|
ImmutableMap.Builder<String, Boolean> results = ImmutableMap.builder();
|
||||||
for (Node n : e.getValue()) {
|
for (Node n : e.getValue()) {
|
||||||
results.put(n.getPermission(), n.getValuePrimitive());
|
results.put(n.getPermission(), n.getValuePrimitive());
|
||||||
}
|
|
||||||
perms.put(e.getKey(), results);
|
|
||||||
}
|
}
|
||||||
|
perms.put(e.getKey(), results);
|
||||||
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, Boolean>> map = ImmutableMap.builder();
|
|
||||||
for (Map.Entry<ImmutableContextSet, ImmutableMap.Builder<String, Boolean>> e : perms.entrySet()) {
|
|
||||||
map.put(e.getKey(), e.getValue().build());
|
|
||||||
}
|
|
||||||
return map.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, Boolean>> map = ImmutableMap.builder();
|
||||||
|
for (Map.Entry<ImmutableContextSet, ImmutableMap.Builder<String, Boolean>> e : perms.entrySet()) {
|
||||||
|
map.put(e.getKey(), e.getValue().build());
|
||||||
|
}
|
||||||
|
return map.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> setPermission(@NonNull ImmutableContextSet contexts, @NonNull String permission, @NonNull Tristate tristate) {
|
public CompletableFuture<Boolean> setPermission(@NonNull ImmutableContextSet contexts, @NonNull String permission, @NonNull Tristate tristate) {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_PERMISSION)) {
|
if (tristate == Tristate.UNDEFINED) {
|
||||||
if (tristate == Tristate.UNDEFINED) {
|
// Unset
|
||||||
// Unset
|
Node node = NodeFactory.newBuilder(permission).withExtraContext(contexts).build();
|
||||||
Node node = NodeFactory.newBuilder(permission).withExtraContext(contexts).build();
|
|
||||||
|
|
||||||
if (enduring) {
|
|
||||||
holder.unsetPermission(node);
|
|
||||||
} else {
|
|
||||||
holder.unsetTransientPermission(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Node node = NodeFactory.newBuilder(permission).setValue(tristate.asBoolean()).withExtraContext(contexts).build();
|
|
||||||
|
|
||||||
// Workaround: unset the inverse, to allow false -> true, true -> false overrides.
|
|
||||||
if (enduring) {
|
if (enduring) {
|
||||||
holder.unsetPermission(node);
|
holder.unsetPermission(node);
|
||||||
} else {
|
} else {
|
||||||
holder.unsetTransientPermission(node);
|
holder.unsetTransientPermission(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enduring) {
|
|
||||||
holder.setPermission(node);
|
|
||||||
} else {
|
|
||||||
holder.setTransientPermission(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
return objectSave(holder).thenApply(v -> true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node node = NodeFactory.newBuilder(permission).setValue(tristate.asBoolean()).withExtraContext(contexts).build();
|
||||||
|
|
||||||
|
// Workaround: unset the inverse, to allow false -> true, true -> false overrides.
|
||||||
|
if (enduring) {
|
||||||
|
holder.unsetPermission(node);
|
||||||
|
} else {
|
||||||
|
holder.unsetTransientPermission(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enduring) {
|
||||||
|
holder.setPermission(node);
|
||||||
|
} else {
|
||||||
|
holder.setTransientPermission(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectSave(holder).thenApply(v -> true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> clearPermissions() {
|
public CompletableFuture<Boolean> clearPermissions() {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) {
|
boolean ret;
|
||||||
boolean ret;
|
if (enduring) {
|
||||||
if (enduring) {
|
ret = holder.clearNodes();
|
||||||
ret = holder.clearNodes();
|
} else {
|
||||||
} else {
|
ret = holder.clearTransientNodes();
|
||||||
ret = holder.clearTransientNodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
return CompletableFuture.completedFuture(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (holder instanceof User) {
|
|
||||||
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
return CompletableFuture.completedFuture(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holder instanceof User) {
|
||||||
|
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectSave(holder).thenApply(v -> true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> clearPermissions(@NonNull ImmutableContextSet set) {
|
public CompletableFuture<Boolean> clearPermissions(@NonNull ImmutableContextSet set) {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) {
|
boolean ret;
|
||||||
boolean ret;
|
if (enduring) {
|
||||||
|
ret = holder.clearNodes(set);
|
||||||
|
} else {
|
||||||
|
List<Node> toRemove = streamNodes(false)
|
||||||
|
.filter(n -> n.getFullContexts().equals(set))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (enduring) {
|
toRemove.forEach(makeUnsetConsumer(false));
|
||||||
ret = holder.clearNodes(set);
|
ret = !toRemove.isEmpty();
|
||||||
} else {
|
|
||||||
List<Node> toRemove = streamNodes(false)
|
|
||||||
.filter(n -> n.getFullContexts().equals(set))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
toRemove.forEach(makeUnsetConsumer(false));
|
|
||||||
ret = !toRemove.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
return CompletableFuture.completedFuture(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (holder instanceof User) {
|
|
||||||
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
return CompletableFuture.completedFuture(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holder instanceof User) {
|
||||||
|
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectSave(holder).thenApply(v -> true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableMap<ImmutableContextSet, ImmutableList<SubjectReference>> getAllParents() {
|
public ImmutableMap<ImmutableContextSet, ImmutableList<SubjectReference>> getAllParents() {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PARENTS)) {
|
Map<ImmutableContextSet, ImmutableList.Builder<SubjectReference>> parents = new HashMap<>();
|
||||||
Map<ImmutableContextSet, ImmutableList.Builder<SubjectReference>> parents = new HashMap<>();
|
|
||||||
|
|
||||||
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) {
|
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) {
|
||||||
ImmutableList.Builder<SubjectReference> results = ImmutableList.builder();
|
ImmutableList.Builder<SubjectReference> results = ImmutableList.builder();
|
||||||
for (Node n : e.getValue()) {
|
for (Node n : e.getValue()) {
|
||||||
if (n.isGroupNode()) {
|
if (n.isGroupNode()) {
|
||||||
results.add(service.getGroupSubjects().loadSubject(n.getGroupName()).join().toReference());
|
results.add(service.getGroupSubjects().loadSubject(n.getGroupName()).join().toReference());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
parents.put(e.getKey(), results);
|
|
||||||
}
|
}
|
||||||
|
parents.put(e.getKey(), results);
|
||||||
ImmutableMap.Builder<ImmutableContextSet, ImmutableList<SubjectReference>> map = ImmutableMap.builder();
|
|
||||||
for (Map.Entry<ImmutableContextSet, ImmutableList.Builder<SubjectReference>> e : parents.entrySet()) {
|
|
||||||
map.put(e.getKey(), e.getValue().build());
|
|
||||||
}
|
|
||||||
return map.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImmutableMap.Builder<ImmutableContextSet, ImmutableList<SubjectReference>> map = ImmutableMap.builder();
|
||||||
|
for (Map.Entry<ImmutableContextSet, ImmutableList.Builder<SubjectReference>> e : parents.entrySet()) {
|
||||||
|
map.put(e.getKey(), e.getValue().build());
|
||||||
|
}
|
||||||
|
return map.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> addParent(@NonNull ImmutableContextSet contexts, @NonNull SubjectReference subject) {
|
public CompletableFuture<Boolean> addParent(@NonNull ImmutableContextSet contexts, @NonNull SubjectReference subject) {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_ADD_PARENT)) {
|
if (subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) {
|
||||||
if (subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) {
|
return subject.resolveLp().thenCompose(sub -> {
|
||||||
return subject.resolveLp().thenCompose(sub -> {
|
DataMutateResult result;
|
||||||
DataMutateResult result;
|
|
||||||
|
|
||||||
if (enduring) {
|
if (enduring) {
|
||||||
result = holder.setPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
|
result = holder.setPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
|
||||||
.withExtraContext(contexts)
|
.withExtraContext(contexts)
|
||||||
.build());
|
.build());
|
||||||
} else {
|
} else {
|
||||||
result = holder.setTransientPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
|
result = holder.setTransientPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
|
||||||
.withExtraContext(contexts)
|
.withExtraContext(contexts)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.asBoolean()) {
|
if (!result.asBoolean()) {
|
||||||
return CompletableFuture.completedFuture(false);
|
return CompletableFuture.completedFuture(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
return objectSave(holder).thenApply(v -> true);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
return CompletableFuture.completedFuture(false);
|
|
||||||
}
|
}
|
||||||
|
return CompletableFuture.completedFuture(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> removeParent(@NonNull ImmutableContextSet contexts, @NonNull SubjectReference subject) {
|
public CompletableFuture<Boolean> removeParent(@NonNull ImmutableContextSet contexts, @NonNull SubjectReference subject) {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_REMOVE_PARENT)) {
|
if (subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) {
|
||||||
if (subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) {
|
subject.resolveLp().thenCompose(sub -> {
|
||||||
subject.resolveLp().thenCompose(sub -> {
|
DataMutateResult result;
|
||||||
DataMutateResult result;
|
|
||||||
|
|
||||||
if (enduring) {
|
if (enduring) {
|
||||||
result = holder.unsetPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
|
result = holder.unsetPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
|
||||||
.withExtraContext(contexts)
|
.withExtraContext(contexts)
|
||||||
.build());
|
.build());
|
||||||
} else {
|
} else {
|
||||||
result = holder.unsetTransientPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
|
result = holder.unsetTransientPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
|
||||||
.withExtraContext(contexts)
|
.withExtraContext(contexts)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.asBoolean()) {
|
if (!result.asBoolean()) {
|
||||||
return CompletableFuture.completedFuture(false);
|
return CompletableFuture.completedFuture(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
return objectSave(holder).thenApply(v -> true);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
return CompletableFuture.completedFuture(false);
|
|
||||||
}
|
}
|
||||||
|
return CompletableFuture.completedFuture(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> clearParents() {
|
public CompletableFuture<Boolean> clearParents() {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PARENTS)) {
|
boolean ret;
|
||||||
boolean ret;
|
if (enduring) {
|
||||||
|
ret = holder.clearParents(true);
|
||||||
|
} else {
|
||||||
|
List<Node> toRemove = streamNodes(false)
|
||||||
|
.filter(Node::isGroupNode)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (enduring) {
|
toRemove.forEach(makeUnsetConsumer(false));
|
||||||
ret = holder.clearParents(true);
|
ret = !toRemove.isEmpty();
|
||||||
} else {
|
|
||||||
List<Node> toRemove = streamNodes(false)
|
|
||||||
.filter(Node::isGroupNode)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
toRemove.forEach(makeUnsetConsumer(false));
|
if (ret && holder instanceof User) {
|
||||||
ret = !toRemove.isEmpty();
|
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
|
||||||
|
|
||||||
if (ret && holder instanceof User) {
|
|
||||||
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
return CompletableFuture.completedFuture(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
return CompletableFuture.completedFuture(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectSave(holder).thenApply(v -> true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> clearParents(@NonNull ImmutableContextSet set) {
|
public CompletableFuture<Boolean> clearParents(@NonNull ImmutableContextSet set) {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PARENTS)) {
|
boolean ret;
|
||||||
boolean ret;
|
if (enduring) {
|
||||||
if (enduring) {
|
ret = holder.clearParents(set, true);
|
||||||
ret = holder.clearParents(set, true);
|
} else {
|
||||||
} else {
|
List<Node> toRemove = streamNodes(false)
|
||||||
List<Node> toRemove = streamNodes(false)
|
.filter(Node::isGroupNode)
|
||||||
.filter(Node::isGroupNode)
|
.filter(n -> n.getFullContexts().equals(set))
|
||||||
.filter(n -> n.getFullContexts().equals(set))
|
.collect(Collectors.toList());
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
toRemove.forEach(makeUnsetConsumer(false));
|
toRemove.forEach(makeUnsetConsumer(false));
|
||||||
ret = !toRemove.isEmpty();
|
ret = !toRemove.isEmpty();
|
||||||
|
|
||||||
if (ret && holder instanceof User) {
|
if (ret && holder instanceof User) {
|
||||||
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
|
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
return CompletableFuture.completedFuture(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
return CompletableFuture.completedFuture(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectSave(holder).thenApply(v -> true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, String>> getAllOptions() {
|
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, String>> getAllOptions() {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_OPTIONS)) {
|
Map<ImmutableContextSet, Map<String, String>> options = new HashMap<>();
|
||||||
Map<ImmutableContextSet, Map<String, String>> options = new HashMap<>();
|
Map<ImmutableContextSet, Integer> minPrefixPriority = new HashMap<>();
|
||||||
Map<ImmutableContextSet, Integer> minPrefixPriority = new HashMap<>();
|
Map<ImmutableContextSet, Integer> minSuffixPriority = new HashMap<>();
|
||||||
Map<ImmutableContextSet, Integer> minSuffixPriority = new HashMap<>();
|
|
||||||
|
|
||||||
for (Node n : enduring ? holder.getEnduringNodes().values() : holder.getTransientNodes().values()) {
|
for (Node n : enduring ? holder.getEnduringNodes().values() : holder.getTransientNodes().values()) {
|
||||||
if (!n.getValuePrimitive()) continue;
|
if (!n.getValuePrimitive()) continue;
|
||||||
if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue;
|
if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue;
|
||||||
|
|
||||||
ImmutableContextSet immutableContexts = n.getFullContexts().makeImmutable();
|
ImmutableContextSet immutableContexts = n.getFullContexts().makeImmutable();
|
||||||
|
|
||||||
if (!options.containsKey(immutableContexts)) {
|
if (!options.containsKey(immutableContexts)) {
|
||||||
options.put(immutableContexts, new HashMap<>());
|
options.put(immutableContexts, new HashMap<>());
|
||||||
minPrefixPriority.put(immutableContexts, Integer.MIN_VALUE);
|
minPrefixPriority.put(immutableContexts, Integer.MIN_VALUE);
|
||||||
minSuffixPriority.put(immutableContexts, Integer.MIN_VALUE);
|
minSuffixPriority.put(immutableContexts, Integer.MIN_VALUE);
|
||||||
}
|
|
||||||
|
|
||||||
if (n.isPrefix()) {
|
|
||||||
Map.Entry<Integer, String> value = n.getPrefix();
|
|
||||||
if (value.getKey() > minPrefixPriority.get(immutableContexts)) {
|
|
||||||
options.get(immutableContexts).put("prefix", value.getValue());
|
|
||||||
minPrefixPriority.put(immutableContexts, value.getKey());
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n.isSuffix()) {
|
|
||||||
Map.Entry<Integer, String> value = n.getSuffix();
|
|
||||||
if (value.getKey() > minSuffixPriority.get(immutableContexts)) {
|
|
||||||
options.get(immutableContexts).put("suffix", value.getValue());
|
|
||||||
minSuffixPriority.put(immutableContexts, value.getKey());
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n.isMeta()) {
|
|
||||||
Map.Entry<String, String> meta = n.getMeta();
|
|
||||||
options.get(immutableContexts).put(meta.getKey(), meta.getValue());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, String>> map = ImmutableMap.builder();
|
if (n.isPrefix()) {
|
||||||
for (Map.Entry<ImmutableContextSet, Map<String, String>> e : options.entrySet()) {
|
Map.Entry<Integer, String> value = n.getPrefix();
|
||||||
map.put(e.getKey(), ImmutableMap.copyOf(e.getValue()));
|
if (value.getKey() > minPrefixPriority.get(immutableContexts)) {
|
||||||
|
options.get(immutableContexts).put("prefix", value.getValue());
|
||||||
|
minPrefixPriority.put(immutableContexts, value.getKey());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n.isSuffix()) {
|
||||||
|
Map.Entry<Integer, String> value = n.getSuffix();
|
||||||
|
if (value.getKey() > minSuffixPriority.get(immutableContexts)) {
|
||||||
|
options.get(immutableContexts).put("suffix", value.getValue());
|
||||||
|
minSuffixPriority.put(immutableContexts, value.getKey());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n.isMeta()) {
|
||||||
|
Map.Entry<String, String> meta = n.getMeta();
|
||||||
|
options.get(immutableContexts).put(meta.getKey(), meta.getValue());
|
||||||
}
|
}
|
||||||
return map.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, String>> map = ImmutableMap.builder();
|
||||||
|
for (Map.Entry<ImmutableContextSet, Map<String, String>> e : options.entrySet()) {
|
||||||
|
map.put(e.getKey(), ImmutableMap.copyOf(e.getValue()));
|
||||||
|
}
|
||||||
|
return map.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> setOption(@NonNull ImmutableContextSet context, @NonNull String key, @NonNull String value) {
|
public CompletableFuture<Boolean> setOption(@NonNull ImmutableContextSet context, @NonNull String key, @NonNull String value) {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_OPTION)) {
|
if (key.equalsIgnoreCase("prefix") || key.equalsIgnoreCase("suffix")) {
|
||||||
if (key.equalsIgnoreCase("prefix") || key.equalsIgnoreCase("suffix")) {
|
// special handling.
|
||||||
// special handling.
|
ChatMetaType type = ChatMetaType.valueOf(key.toUpperCase());
|
||||||
ChatMetaType type = ChatMetaType.valueOf(key.toUpperCase());
|
|
||||||
|
|
||||||
// remove all prefixes/suffixes from the user
|
// remove all prefixes/suffixes from the user
|
||||||
List<Node> toRemove = streamNodes(enduring)
|
List<Node> toRemove = streamNodes(enduring)
|
||||||
.filter(type::matches)
|
.filter(type::matches)
|
||||||
.filter(n -> n.getFullContexts().equals(context))
|
.filter(n -> n.getFullContexts().equals(context))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
toRemove.forEach(makeUnsetConsumer(enduring));
|
toRemove.forEach(makeUnsetConsumer(enduring));
|
||||||
|
|
||||||
MetaAccumulator metaAccumulator = holder.accumulateMeta(null, null, ExtractedContexts.generate(service.calculateContexts(context)));
|
MetaAccumulator metaAccumulator = holder.accumulateMeta(null, null, ExtractedContexts.generate(service.calculateContexts(context)));
|
||||||
int priority = metaAccumulator.getChatMeta(type).keySet().stream().mapToInt(e -> e).max().orElse(0);
|
int priority = metaAccumulator.getChatMeta(type).keySet().stream().mapToInt(e -> e).max().orElse(0);
|
||||||
priority += 10;
|
priority += 10;
|
||||||
|
|
||||||
if (enduring) {
|
|
||||||
holder.setPermission(NodeFactory.makeChatMetaNode(type, priority, value).withExtraContext(context).build());
|
|
||||||
} else {
|
|
||||||
holder.setTransientPermission(NodeFactory.makeChatMetaNode(type, priority, value).withExtraContext(context).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (enduring) {
|
||||||
|
holder.setPermission(NodeFactory.makeChatMetaNode(type, priority, value).withExtraContext(context).build());
|
||||||
} else {
|
} else {
|
||||||
// standard remove
|
holder.setTransientPermission(NodeFactory.makeChatMetaNode(type, priority, value).withExtraContext(context).build());
|
||||||
List<Node> toRemove = streamNodes(enduring)
|
|
||||||
.filter(n -> n.isMeta() && n.getMeta().getKey().equals(key))
|
|
||||||
.filter(n -> n.getFullContexts().equals(context))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
toRemove.forEach(makeUnsetConsumer(enduring));
|
|
||||||
|
|
||||||
if (enduring) {
|
|
||||||
holder.setPermission(NodeFactory.makeMetaNode(key, value).withExtraContext(context).build());
|
|
||||||
} else {
|
|
||||||
holder.setTransientPermission(NodeFactory.makeMetaNode(key, value).withExtraContext(context).build());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
} else {
|
||||||
|
// standard remove
|
||||||
|
List<Node> toRemove = streamNodes(enduring)
|
||||||
|
.filter(n -> n.isMeta() && n.getMeta().getKey().equals(key))
|
||||||
|
.filter(n -> n.getFullContexts().equals(context))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
toRemove.forEach(makeUnsetConsumer(enduring));
|
||||||
|
|
||||||
|
if (enduring) {
|
||||||
|
holder.setPermission(NodeFactory.makeMetaNode(key, value).withExtraContext(context).build());
|
||||||
|
} else {
|
||||||
|
holder.setTransientPermission(NodeFactory.makeMetaNode(key, value).withExtraContext(context).build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return objectSave(holder).thenApply(v -> true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> unsetOption(ImmutableContextSet set, String key) {
|
public CompletableFuture<Boolean> unsetOption(ImmutableContextSet set, String key) {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_OPTION)) {
|
List<Node> toRemove = streamNodes(enduring)
|
||||||
List<Node> toRemove = streamNodes(enduring)
|
.filter(n -> {
|
||||||
.filter(n -> {
|
if (key.equalsIgnoreCase("prefix")) {
|
||||||
if (key.equalsIgnoreCase("prefix")) {
|
return n.isPrefix();
|
||||||
return n.isPrefix();
|
} else if (key.equalsIgnoreCase("suffix")) {
|
||||||
} else if (key.equalsIgnoreCase("suffix")) {
|
return n.isSuffix();
|
||||||
return n.isSuffix();
|
} else {
|
||||||
} else {
|
return n.isMeta() && n.getMeta().getKey().equals(key);
|
||||||
return n.isMeta() && n.getMeta().getKey().equals(key);
|
}
|
||||||
}
|
})
|
||||||
})
|
.filter(n -> n.getFullContexts().equals(set))
|
||||||
.filter(n -> n.getFullContexts().equals(set))
|
.collect(Collectors.toList());
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
toRemove.forEach(makeUnsetConsumer(enduring));
|
toRemove.forEach(makeUnsetConsumer(enduring));
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> true);
|
return objectSave(holder).thenApply(v -> true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> clearOptions(@NonNull ImmutableContextSet set) {
|
public CompletableFuture<Boolean> clearOptions(@NonNull ImmutableContextSet set) {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_OPTIONS)) {
|
List<Node> toRemove = streamNodes(enduring)
|
||||||
List<Node> toRemove = streamNodes(enduring)
|
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
|
||||||
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
|
.filter(n -> n.getFullContexts().equals(set))
|
||||||
.filter(n -> n.getFullContexts().equals(set))
|
.collect(Collectors.toList());
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
toRemove.forEach(makeUnsetConsumer(enduring));
|
toRemove.forEach(makeUnsetConsumer(enduring));
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> !toRemove.isEmpty());
|
return objectSave(holder).thenApply(v -> !toRemove.isEmpty());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Boolean> clearOptions() {
|
public CompletableFuture<Boolean> clearOptions() {
|
||||||
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_OPTIONS)) {
|
List<Node> toRemove = streamNodes(enduring)
|
||||||
List<Node> toRemove = streamNodes(enduring)
|
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
|
||||||
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
|
.collect(Collectors.toList());
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
toRemove.forEach(makeUnsetConsumer(enduring));
|
toRemove.forEach(makeUnsetConsumer(enduring));
|
||||||
|
|
||||||
return objectSave(holder).thenApply(v -> !toRemove.isEmpty());
|
return objectSave(holder).thenApply(v -> !toRemove.isEmpty());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<Node> streamNodes(boolean enduring) {
|
private Stream<Node> streamNodes(boolean enduring) {
|
||||||
|
@ -40,6 +40,7 @@ import me.lucko.luckperms.api.context.ContextSet;
|
|||||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||||
import me.lucko.luckperms.common.calculators.PermissionCalculator;
|
import me.lucko.luckperms.common.calculators.PermissionCalculator;
|
||||||
import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
|
import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
|
||||||
|
import me.lucko.luckperms.common.contexts.ContextSetComparator;
|
||||||
import me.lucko.luckperms.common.processors.MapProcessor;
|
import me.lucko.luckperms.common.processors.MapProcessor;
|
||||||
import me.lucko.luckperms.common.processors.PermissionProcessor;
|
import me.lucko.luckperms.common.processors.PermissionProcessor;
|
||||||
import me.lucko.luckperms.common.verbose.CheckOrigin;
|
import me.lucko.luckperms.common.verbose.CheckOrigin;
|
||||||
@ -49,7 +50,6 @@ import me.lucko.luckperms.sponge.service.model.LPSubject;
|
|||||||
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
|
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -65,7 +65,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class CalculatedSubjectData implements LPSubjectData {
|
public class CalculatedSubjectData implements LPSubjectData {
|
||||||
private static final ContextComparator CONTEXT_COMPARATOR = new ContextComparator();
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final LPSubject parentSubject;
|
private final LPSubject parentSubject;
|
||||||
@ -87,7 +86,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
|||||||
processors.add(new SpongeWildcardProcessor());
|
processors.add(new SpongeWildcardProcessor());
|
||||||
|
|
||||||
CalculatorHolder holder = new CalculatorHolder(new PermissionCalculator(service.getPlugin(), PermissionCalculatorMetadata.of(calculatorDisplayName, contexts), processors.build()));
|
CalculatorHolder holder = new CalculatorHolder(new PermissionCalculator(service.getPlugin(), PermissionCalculatorMetadata.of(calculatorDisplayName, contexts), processors.build()));
|
||||||
holder.setPermissions(flattenMap(contexts, permissions));
|
holder.setPermissions(flattenMap(getRelevantEntries(contexts, permissions)));
|
||||||
|
|
||||||
return holder;
|
return holder;
|
||||||
}
|
}
|
||||||
@ -290,11 +289,10 @@ public class CalculatedSubjectData implements LPSubjectData {
|
|||||||
return CompletableFuture.completedFuture(!map.isEmpty());
|
return CompletableFuture.completedFuture(!map.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <V> Map<String, V> flattenMap(ContextSet contexts, Map<ImmutableContextSet, Map<String, V>> source) {
|
private static <V> Map<String, V> flattenMap(SortedMap<ImmutableContextSet, Map<String, V>> data) {
|
||||||
Map<String, V> map = new HashMap<>();
|
Map<String, V> map = new HashMap<>();
|
||||||
|
|
||||||
SortedMap<ImmutableContextSet, Map<String, V>> ret = getRelevantEntries(contexts, source);
|
for (Map<String, V> m : data.values()) {
|
||||||
for (Map<String, V> m : ret.values()) {
|
|
||||||
for (Map.Entry<String, V> e : m.entrySet()) {
|
for (Map.Entry<String, V> e : m.entrySet()) {
|
||||||
map.putIfAbsent(e.getKey(), e.getValue());
|
map.putIfAbsent(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
@ -304,7 +302,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static <K, V> SortedMap<ImmutableContextSet, Map<K, V>> getRelevantEntries(ContextSet set, Map<ImmutableContextSet, Map<K, V>> map) {
|
private static <K, V> SortedMap<ImmutableContextSet, Map<K, V>> getRelevantEntries(ContextSet set, Map<ImmutableContextSet, Map<K, V>> map) {
|
||||||
ImmutableSortedMap.Builder<ImmutableContextSet, Map<K, V>> perms = ImmutableSortedMap.orderedBy(CONTEXT_COMPARATOR);
|
ImmutableSortedMap.Builder<ImmutableContextSet, Map<K, V>> perms = ImmutableSortedMap.orderedBy(ContextSetComparator.reverse());
|
||||||
|
|
||||||
for (Map.Entry<ImmutableContextSet, Map<K, V>> e : map.entrySet()) {
|
for (Map.Entry<ImmutableContextSet, Map<K, V>> e : map.entrySet()) {
|
||||||
if (!e.getKey().isSatisfiedBy(set)) {
|
if (!e.getKey().isSatisfiedBy(set)) {
|
||||||
@ -321,15 +319,6 @@ public class CalculatedSubjectData implements LPSubjectData {
|
|||||||
return a == null && b == null || a != null && b != null && a.equalsIgnoreCase(b);
|
return a == null && b == null || a != null && b != null && a.equalsIgnoreCase(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ContextComparator implements Comparator<ImmutableContextSet> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(ImmutableContextSet o1, ImmutableContextSet o2) {
|
|
||||||
int i = Integer.compare(o1.size(), o2.size());
|
|
||||||
return i == 0 ? 1 : i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CalculatorHolder {
|
private static class CalculatorHolder {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -29,7 +29,7 @@ import lombok.ToString;
|
|||||||
|
|
||||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||||
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReferenceFactory;
|
||||||
import me.lucko.luckperms.sponge.service.storage.SubjectStorageModel;
|
import me.lucko.luckperms.sponge.service.storage.SubjectStorageModel;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -66,7 +66,7 @@ public class SubjectDataHolder {
|
|||||||
parents.entrySet().stream()
|
parents.entrySet().stream()
|
||||||
.collect(Collectors.toMap(
|
.collect(Collectors.toMap(
|
||||||
k -> ImmutableContextSet.fromMap(k.getKey()),
|
k -> ImmutableContextSet.fromMap(k.getKey()),
|
||||||
v -> v.getValue().stream().map(s -> SubjectReference.deserialize(service, s)).collect(Collectors.toList())
|
v -> v.getValue().stream().map(s -> SubjectReferenceFactory.deserialize(service, s)).collect(Collectors.toList())
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -44,13 +44,10 @@ import me.lucko.luckperms.sponge.service.calculated.PermissionLookup;
|
|||||||
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
import me.lucko.luckperms.sponge.service.model.LPSubject;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||||
import me.lucko.luckperms.sponge.service.storage.SubjectStorageModel;
|
import me.lucko.luckperms.sponge.service.storage.SubjectStorageModel;
|
||||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
|
||||||
|
|
||||||
import org.spongepowered.api.command.CommandSource;
|
import org.spongepowered.api.command.CommandSource;
|
||||||
import org.spongepowered.api.service.permission.Subject;
|
import org.spongepowered.api.service.permission.Subject;
|
||||||
|
|
||||||
import co.aikar.timings.Timing;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -246,46 +243,36 @@ public class PersistedSubject implements LPSubject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tristate getPermissionValue(@NonNull ImmutableContextSet contexts, @NonNull String node) {
|
public Tristate getPermissionValue(@NonNull ImmutableContextSet contexts, @NonNull String node) {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PERMISSION_VALUE)) {
|
Tristate t = permissionLookupCache.get(PermissionLookup.of(node, contexts));
|
||||||
Tristate t = permissionLookupCache.get(PermissionLookup.of(node, contexts));
|
service.getPlugin().getVerboseHandler().offerCheckData(CheckOrigin.INTERNAL, "local:" + getParentCollection().getIdentifier() + "/" + identifier, contexts, node, t);
|
||||||
service.getPlugin().getVerboseHandler().offerCheckData(CheckOrigin.INTERNAL, "local:" + getParentCollection().getIdentifier() + "/" + identifier, contexts, node, t);
|
return t;
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isChildOf(@NonNull ImmutableContextSet contexts, @NonNull SubjectReference subject) {
|
public boolean isChildOf(@NonNull ImmutableContextSet contexts, @NonNull SubjectReference subject) {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_IS_CHILD_OF)) {
|
if (getParentCollection().getIdentifier().equalsIgnoreCase("defaults")) {
|
||||||
if (getParentCollection().getIdentifier().equalsIgnoreCase("defaults")) {
|
return subjectData.getParents(contexts).contains(subject) ||
|
||||||
return subjectData.getParents(contexts).contains(subject) ||
|
transientSubjectData.getParents(contexts).contains(subject);
|
||||||
transientSubjectData.getParents(contexts).contains(subject);
|
} else {
|
||||||
} else {
|
return subjectData.getParents(contexts).contains(subject) ||
|
||||||
return subjectData.getParents(contexts).contains(subject) ||
|
transientSubjectData.getParents(contexts).contains(subject) ||
|
||||||
transientSubjectData.getParents(contexts).contains(subject) ||
|
getParentCollection().getDefaults().getParents(contexts).contains(subject) ||
|
||||||
getParentCollection().getDefaults().getParents(contexts).contains(subject) ||
|
service.getDefaults().getParents(contexts).contains(subject);
|
||||||
service.getDefaults().getParents(contexts).contains(subject);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<SubjectReference> getParents(@NonNull ImmutableContextSet contexts) {
|
public ImmutableList<SubjectReference> getParents(@NonNull ImmutableContextSet contexts) {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PARENTS)) {
|
return parentLookupCache.get(contexts);
|
||||||
return parentLookupCache.get(contexts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> getOption(ImmutableContextSet contexts, String key) {
|
public Optional<String> getOption(ImmutableContextSet contexts, String key) {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_OPTION)) {
|
return optionLookupCache.get(OptionLookup.of(key, contexts));
|
||||||
return optionLookupCache.get(OptionLookup.of(key, contexts));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableContextSet getActiveContextSet() {
|
public ImmutableContextSet getActiveContextSet() {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_ACTIVE_CONTEXTS)) {
|
return service.getPlugin().getContextManager().getApplicableContext(sponge()).makeImmutable();
|
||||||
return service.getPlugin().getContextManager().getApplicableContext(sponge()).makeImmutable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ import me.lucko.luckperms.common.node.NodeWithContextComparator;
|
|||||||
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
|
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
|
||||||
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
||||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||||
|
import me.lucko.luckperms.sponge.service.model.SubjectReferenceFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -164,7 +165,7 @@ public class SubjectStorageModel {
|
|||||||
String collection = parent.get("collection").getAsString();
|
String collection = parent.get("collection").getAsString();
|
||||||
String subject = parent.get("subject").getAsString();
|
String subject = parent.get("subject").getAsString();
|
||||||
|
|
||||||
pars.add(SubjectReference.of(service, collection, subject));
|
pars.add(SubjectReferenceFactory.obtain(service, collection, subject));
|
||||||
}
|
}
|
||||||
|
|
||||||
parentsBuilder.put(contextSet, pars.build());
|
parentsBuilder.put(contextSet, pars.build());
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of LuckPerms, licensed under the MIT License.
|
|
||||||
*
|
|
||||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
|
||||||
* Copyright (c) contributors
|
|
||||||
*
|
|
||||||
* 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.sponge.timings;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
|
||||||
public enum LPTiming {
|
|
||||||
|
|
||||||
GET_SUBJECTS("getSubjects"),
|
|
||||||
|
|
||||||
USER_COLLECTION_GET("userCollectionGet"),
|
|
||||||
GROUP_COLLECTION_GET("groupCollectionGet"),
|
|
||||||
|
|
||||||
USER_GET_PERMISSION_VALUE("userGetPermissionValue"),
|
|
||||||
USER_GET_PARENTS("userGetParents"),
|
|
||||||
USER_IS_CHILD_OF("userIsChildOf"),
|
|
||||||
USER_GET_OPTION("userGetOption"),
|
|
||||||
USER_GET_ACTIVE_CONTEXTS("userGetActiveContexts"),
|
|
||||||
|
|
||||||
GROUP_GET_PERMISSION_VALUE("groupGetPermissionValue"),
|
|
||||||
GROUP_GET_PARENTS("groupGetParents"),
|
|
||||||
GROUP_IS_CHILD_OF("groupIsChildOf"),
|
|
||||||
GROUP_GET_OPTION("groupGetOption"),
|
|
||||||
GROUP_GET_ACTIVE_CONTEXTS("groupGetActiveContexts"),
|
|
||||||
|
|
||||||
LP_SUBJECT_GET_PERMISSIONS("lpSubjectGetPermissions"),
|
|
||||||
LP_SUBJECT_SET_PERMISSION("lpSubjectSetPermission"),
|
|
||||||
LP_SUBJECT_CLEAR_PERMISSIONS("lpSubjectClearPermissions"),
|
|
||||||
LP_SUBJECT_GET_PARENTS("lpSubjectGetParents"),
|
|
||||||
LP_SUBJECT_ADD_PARENT("lpSubjectAddParent"),
|
|
||||||
LP_SUBJECT_REMOVE_PARENT("lpSubjectRemoveParent"),
|
|
||||||
LP_SUBJECT_CLEAR_PARENTS("lpSubjectClearParents"),
|
|
||||||
LP_SUBJECT_GET_OPTIONS("lpSubjectGetOptions"),
|
|
||||||
LP_SUBJECT_SET_OPTION("lpSubjectSetOption"),
|
|
||||||
LP_SUBJECT_CLEAR_OPTIONS("lpSubjectClearOptions"),
|
|
||||||
|
|
||||||
INTERNAL_SUBJECT_GET_PERMISSION_VALUE("internalSubjectGetPermissionValue"),
|
|
||||||
INTERNAL_SUBJECT_IS_CHILD_OF("internalSubjectIsChildOf"),
|
|
||||||
INTERNAL_SUBJECT_GET_PARENTS("internalSubjectGetParents"),
|
|
||||||
INTERNAL_SUBJECT_GET_OPTION("internalSubjectGetOption"),
|
|
||||||
INTERNAL_SUBJECT_GET_ACTIVE_CONTEXTS("internalSubjectGetActiveContexts"),
|
|
||||||
|
|
||||||
ON_COMMAND("onCommand"),
|
|
||||||
COMMAND_TAB_COMPLETE("commandTabComplete"),
|
|
||||||
ON_CLIENT_LOGIN("onClientLogin"),
|
|
||||||
ON_CLIENT_LEAVE("onClientLeave");
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of LuckPerms, licensed under the MIT License.
|
|
||||||
*
|
|
||||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
|
||||||
* Copyright (c) contributors
|
|
||||||
*
|
|
||||||
* 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.sponge.timings;
|
|
||||||
|
|
||||||
import lombok.NonNull;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
import me.lucko.luckperms.sponge.LPSpongePlugin;
|
|
||||||
|
|
||||||
import co.aikar.timings.Timing;
|
|
||||||
import co.aikar.timings.Timings;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class LPTimings {
|
|
||||||
private final Map<LPTiming, Timing> timings;
|
|
||||||
|
|
||||||
public LPTimings(LPSpongePlugin plugin) {
|
|
||||||
ImmutableMap.Builder<LPTiming, Timing> map = ImmutableMap.builder();
|
|
||||||
for (LPTiming t : LPTiming.values()) {
|
|
||||||
map.put(t, Timings.of(plugin, t.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
timings = map.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Timing time(@NonNull LPTiming timing) {
|
|
||||||
Timing t = timings.get(timing);
|
|
||||||
t.startTimingIfSync();
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user