Cache SubjectReference instances, general cleanup

This commit is contained in:
Luck 2017-09-21 21:59:27 +01:00
parent f9efa15781
commit 777c972bdc
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
36 changed files with 732 additions and 780 deletions

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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() {

View File

@ -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() {

View File

@ -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() {

View File

@ -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

View File

@ -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) {

View File

@ -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() {

View File

@ -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);
} }

View File

@ -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 {

View File

@ -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()) {

View File

@ -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) {

View File

@ -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;

View File

@ -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)
*/ */

View File

@ -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

View File

@ -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();
} }

View File

@ -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) {

View File

@ -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 {

View File

@ -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();
}
}
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) { if (s != null) {
return s; return s;
} }
}
}
LPSubject s = service.getCollection(collectionIdentifier).loadSubject(subjectIdentifier).join(); // 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 (sinceLast < TimeUnit.SECONDS.toMillis(10)) {
if (cache != null) {
LPSubject s = cache.get();
if (s != null) { if (s != null) {
return CompletableFuture.completedFuture(s); 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 (sinceLast < TimeUnit.SECONDS.toMillis(10)) {
if (cache != null) {
LPSubject s = cache.get();
if (s != null) { if (s != null) {
return CompletableFuture.completedFuture(s.sponge()); 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 + ")";
}
} }

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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,9 +54,7 @@ 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 {
try (Timing ignored = plugin.getTimings().time(LPTiming.ON_COMMAND)) {
List<String> args = Util.stripQuotes(Splitter.on(COMMAND_SEPARATOR_PATTERN).omitEmptyStrings().splitToList(s)); List<String> args = Util.stripQuotes(Splitter.on(COMMAND_SEPARATOR_PATTERN).omitEmptyStrings().splitToList(s));
// resolve selectors // resolve selectors
@ -83,31 +77,33 @@ class SpongeCommand extends CommandManager implements CommandCallable {
} }
} }
onCommand(plugin.getSenderFactory().wrap(source), "lp", args); return args;
return CommandResult.success();
} }
@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

View File

@ -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,7 +139,6 @@ 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.) */
@ -189,7 +185,6 @@ public class SpongeListener {
}); });
} }
} }
}
@Listener(order = Order.LAST) @Listener(order = Order.LAST)
@IsCancelled(Tristate.UNDEFINED) @IsCancelled(Tristate.UNDEFINED)
@ -218,13 +213,12 @@ 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(); final UuidCache cache = plugin.getUuidCache();
// Unload the user from memory when they disconnect // Unload the user from memory when they disconnect
cache.clearCache(e.getTargetEntity().getUniqueId()); cache.clearCache(e.getTargetEntity().getUniqueId());
} }
}
@Listener @Listener
public void onSendCommand(SendCommandEvent e) { public void onSendCommand(SendCommandEvent e) {

View File

@ -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;

View File

@ -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());
} }
} }
} }

View File

@ -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,7 +173,6 @@ 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) {
@ -191,25 +187,19 @@ public class SpongeGroup extends Group {
t = plugin.getService().getDefaults().getPermissionValue(contexts, permission); t = plugin.getService().getDefaults().getPermissionValue(contexts, permission);
return t; 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);
@ -232,14 +222,11 @@ public class SpongeGroup extends Group {
return plugin.getService().getDefaults().getOption(contexts, s); 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) {
MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, ExtractedContexts.generate(plugin.getService().calculateContexts(contexts))); MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, ExtractedContexts.generate(plugin.getService().calculateContexts(contexts)));

View File

@ -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,21 +126,16 @@ 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()) {
@ -162,11 +154,9 @@ public class SpongeUser extends User {
return getService().sortSubjects(subjects.build()); 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) {
@ -180,8 +170,9 @@ public class SpongeUser extends User {
} }
} }
if (data.getMeta().containsKey(s)) { String val = data.getMeta().get(s);
return Optional.of(data.getMeta().get(s)); if (val != null) {
return Optional.of(val);
} }
Optional<String> v = plugin.getService().getUserSubjects().getDefaults().getOption(contexts, s); Optional<String> v = plugin.getService().getUserSubjects().getDefaults().getOption(contexts, s);
@ -191,14 +182,11 @@ public class SpongeUser extends User {
return plugin.getService().getDefaults().getOption(contexts, s); 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());
} }
} }
}
} }

View File

@ -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,10 +155,8 @@ 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
public ImmutableMap<String, LPSubjectCollection> getLoadedCollections() { public ImmutableMap<String, LPSubjectCollection> getLoadedCollections() {
@ -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

View File

@ -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,7 +72,6 @@ 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()) {
@ -92,11 +88,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
} }
return map.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();
@ -127,11 +121,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
return objectSave(holder).thenApply(v -> true); 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();
@ -149,13 +141,10 @@ public class LuckPermsSubjectData implements LPSubjectData {
return objectSave(holder).thenApply(v -> true); 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) { if (enduring) {
ret = holder.clearNodes(set); ret = holder.clearNodes(set);
} else { } else {
@ -177,11 +166,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
return objectSave(holder).thenApply(v -> true); 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()) {
@ -200,11 +187,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
} }
return map.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;
@ -228,11 +213,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
} }
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;
@ -256,13 +239,10 @@ public class LuckPermsSubjectData implements LPSubjectData {
} }
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) { if (enduring) {
ret = holder.clearParents(true); ret = holder.clearParents(true);
} else { } else {
@ -284,11 +264,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
return objectSave(holder).thenApply(v -> true); 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);
@ -312,11 +290,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
return objectSave(holder).thenApply(v -> true); 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<>();
@ -363,11 +339,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
} }
return map.build(); 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());
@ -408,11 +382,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
return objectSave(holder).thenApply(v -> true); 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")) {
@ -430,11 +402,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
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))
@ -444,11 +414,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
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());
@ -457,7 +425,6 @@ public class LuckPermsSubjectData implements LPSubjectData {
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) {
return (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).values().stream(); return (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).values().stream();

View File

@ -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

View File

@ -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())
)) ))
); );
} }

View File

@ -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,16 +243,13 @@ 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);
@ -266,26 +260,19 @@ public class PersistedSubject implements LPSubject {
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();
} }
}
} }

View File

@ -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());

View File

@ -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;
}

View File

@ -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;
}
}