Use static empty context set where appropriate, cache reversed comparator instances
This commit is contained in:
parent
2baea0ce13
commit
a5e41e479a
@ -163,27 +163,28 @@ public final class ImmutableContextSet implements ContextSet {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated // This set is already immutable!
|
||||
@Nonnull
|
||||
public ImmutableContextSet makeImmutable() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public MutableContextSet mutableCopy() {
|
||||
return MutableContextSet.fromSet(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Map.Entry<String, String>> toSet() {
|
||||
return map.entries();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public Map<String, String> toMap() {
|
||||
ImmutableMap.Builder<String, String> m = ImmutableMap.builder();
|
||||
for (Map.Entry<String, String> e : map.entries()) {
|
||||
@ -193,33 +194,33 @@ public final class ImmutableContextSet implements ContextSet {
|
||||
return m.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public Multimap<String, String> toMultimap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public boolean containsKey(@Nonnull String key) {
|
||||
return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<String> getValues(@Nonnull String key) {
|
||||
Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
|
||||
return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public boolean has(@Nonnull String key, @Nonnull String value) {
|
||||
return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) {
|
||||
value = checkNotNull(value, "value").intern();
|
||||
Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
|
||||
|
@ -198,6 +198,7 @@ public final class MutableContextSet implements ContextSet {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public Map<String, String> toMap() {
|
||||
ImmutableMap.Builder<String, String> m = ImmutableMap.builder();
|
||||
for (Map.Entry<String, String> e : map.entries()) {
|
||||
|
@ -45,7 +45,7 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
|
||||
|
||||
@Getter
|
||||
@Accessors(fluent = true)
|
||||
private ExecutorService asyncLp;
|
||||
private ExecutorService asyncFallback;
|
||||
|
||||
@Getter
|
||||
@Accessors(fluent = true)
|
||||
@ -61,17 +61,17 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean useBukkitAsync = false;
|
||||
private boolean useFallback = true;
|
||||
|
||||
private final Set<BukkitTask> tasks = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public BukkitSchedulerAdapter(LPBukkitPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
this.asyncLp = Executors.newCachedThreadPool();
|
||||
this.asyncBukkit = r -> plugin.getServer().getScheduler().runTaskAsynchronously(plugin, r);
|
||||
this.sync = r -> plugin.getServer().getScheduler().runTask(plugin, r);
|
||||
this.async = r -> (useBukkitAsync ? asyncBukkit : asyncLp).execute(r);
|
||||
this.sync = new SyncExecutor();
|
||||
this.asyncFallback = Executors.newCachedThreadPool();
|
||||
this.asyncBukkit = new BukkitAsyncExecutor();
|
||||
this.async = new AsyncExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,19 +103,45 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
|
||||
|
||||
@Override
|
||||
public void syncLater(Runnable runnable, long delayTicks) {
|
||||
plugin.getServer().getScheduler().runTaskLater(plugin, runnable, delayTicks);
|
||||
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, runnable, delayTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
tasks.forEach(BukkitTask::cancel);
|
||||
|
||||
// wait for executor
|
||||
asyncLp.shutdown();
|
||||
asyncFallback.shutdown();
|
||||
try {
|
||||
asyncLp.awaitTermination(30, TimeUnit.SECONDS);
|
||||
asyncFallback.awaitTermination(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class SyncExecutor implements Executor {
|
||||
@Override
|
||||
public void execute(Runnable runnable) {
|
||||
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private final class AsyncExecutor implements Executor {
|
||||
@Override
|
||||
public void execute(Runnable runnable) {
|
||||
if (useFallback || !plugin.isEnabled()) {
|
||||
asyncFallback.execute(runnable);
|
||||
} else {
|
||||
asyncBukkit.execute(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class BukkitAsyncExecutor implements Executor {
|
||||
@Override
|
||||
public void execute(Runnable runnable) {
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, runnable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
}
|
||||
|
||||
// replace the temporary executor when the Bukkit one starts
|
||||
getServer().getScheduler().runTaskAsynchronously(this, () -> scheduler.setUseBukkitAsync(true));
|
||||
getServer().getScheduler().runTaskAsynchronously(this, () -> scheduler.setUseFallback(false));
|
||||
|
||||
// Load any online users (in the case of a reload)
|
||||
for (Player player : getServer().getOnlinePlayers()) {
|
||||
@ -323,8 +323,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch back to the LP executor, the bukkit one won't allow new tasks
|
||||
scheduler.setUseBukkitAsync(false);
|
||||
// Switch back to the fallback executor, the bukkit one won't allow new tasks
|
||||
scheduler.setUseFallback(true);
|
||||
|
||||
started = false;
|
||||
|
||||
|
@ -40,7 +40,7 @@ public class MetaComparator implements Comparator<Map.Entry<Integer, LocalizedNo
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return NodeWithContextComparator.get().compare(o1.getValue(), o2.getValue());
|
||||
return NodeWithContextComparator.normal().compare(o1.getValue(), o2.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,13 +35,15 @@ import java.util.Map;
|
||||
|
||||
public class ContextSetComparator implements Comparator<ImmutableContextSet> {
|
||||
|
||||
private static final ContextSetComparator INSTANCE = new ContextSetComparator();
|
||||
public static Comparator<ImmutableContextSet> get() {
|
||||
private static final Comparator<ImmutableContextSet> INSTANCE = new ContextSetComparator();
|
||||
private static final Comparator<ImmutableContextSet> REVERSE = INSTANCE.reversed();
|
||||
|
||||
public static Comparator<ImmutableContextSet> normal() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static Comparator<ImmutableContextSet> reverse() {
|
||||
return INSTANCE.reversed();
|
||||
return REVERSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,10 +61,14 @@ public class ContextSetConfigurateSerializer {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static MutableContextSet deserializeContextSet(ConfigurationNode data) {
|
||||
public static ContextSet deserializeContextSet(ConfigurationNode data) {
|
||||
Preconditions.checkArgument(data.hasMapChildren());
|
||||
Map<Object, ? extends ConfigurationNode> dataMap = data.getChildrenMap();
|
||||
|
||||
if (dataMap.isEmpty()) {
|
||||
return ContextSet.empty();
|
||||
}
|
||||
|
||||
MutableContextSet map = MutableContextSet.create();
|
||||
for (Map.Entry<Object, ? extends ConfigurationNode> e : dataMap.entrySet()) {
|
||||
String k = e.getKey().toString();
|
||||
|
@ -28,6 +28,7 @@ package me.lucko.luckperms.common.contexts;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
@ -66,10 +67,28 @@ public class ContextSetJsonSerializer {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static MutableContextSet deserializeContextSet(JsonElement element) {
|
||||
public static ContextSet deserializeContextSet(Gson gson, String json) {
|
||||
Preconditions.checkNotNull(json, "json");
|
||||
if (json.equals("{}")) {
|
||||
return ContextSet.empty();
|
||||
}
|
||||
|
||||
JsonObject context = gson.fromJson(json, JsonObject.class);
|
||||
if (context == null || context.size() == 0) {
|
||||
return ContextSet.empty();
|
||||
}
|
||||
|
||||
return deserializeContextSet(context);
|
||||
}
|
||||
|
||||
public static ContextSet deserializeContextSet(JsonElement element) {
|
||||
Preconditions.checkArgument(element.isJsonObject());
|
||||
JsonObject data = element.getAsJsonObject();
|
||||
|
||||
if (data.size() == 0) {
|
||||
return ContextSet.empty();
|
||||
}
|
||||
|
||||
MutableContextSet map = MutableContextSet.create();
|
||||
for (Map.Entry<String, JsonElement> e : data.entrySet()) {
|
||||
String k = e.getKey();
|
||||
|
@ -26,17 +26,21 @@
|
||||
package me.lucko.luckperms.common.node;
|
||||
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.common.utils.CollationKeyCache;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class NodeComparator implements Comparator<Node> {
|
||||
private static final NodeComparator INSTANCE = new NodeComparator();
|
||||
public static Comparator<Node> get() {
|
||||
|
||||
private static final Comparator<Node> INSTANCE = new NodeComparator();
|
||||
private static final Comparator<Node> REVERSE = INSTANCE.reversed();
|
||||
|
||||
public static Comparator<Node> normal() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static Comparator<Node> reverse() {
|
||||
return INSTANCE.reversed();
|
||||
return REVERSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,7 +65,7 @@ public class NodeComparator implements Comparator<Node> {
|
||||
return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1;
|
||||
}
|
||||
|
||||
return NodeWithContextComparator.get().compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1;
|
||||
return CollationKeyCache.compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -74,13 +74,13 @@ public final class NodeModel {
|
||||
|
||||
public synchronized Node toNode() {
|
||||
if (node == null) {
|
||||
Node.Builder builder = NodeFactory.newBuilder(permission);
|
||||
builder.setValue(value);
|
||||
builder.setServer(server);
|
||||
builder.setWorld(world);
|
||||
builder.setExpiry(expiry);
|
||||
builder.withExtraContext(contexts);
|
||||
node = builder.build();
|
||||
node = NodeFactory.newBuilder(permission)
|
||||
.setValue(value)
|
||||
.setServer(server)
|
||||
.setWorld(world)
|
||||
.setExpiry(expiry)
|
||||
.withExtraContext(contexts)
|
||||
.build();
|
||||
}
|
||||
|
||||
return node;
|
||||
|
@ -28,16 +28,11 @@ package me.lucko.luckperms.common.node;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
|
||||
import me.lucko.luckperms.api.LocalizedNode;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.common.utils.CollationKeyCache;
|
||||
|
||||
import java.text.CollationKey;
|
||||
import java.text.Collator;
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Compares permission nodes based upon their supposed "priority".
|
||||
@ -45,16 +40,16 @@ import java.util.Locale;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class NodeWithContextComparator implements Comparator<LocalizedNode> {
|
||||
|
||||
private static final NodeWithContextComparator INSTANCE = new NodeWithContextComparator();
|
||||
public static NodeWithContextComparator get() {
|
||||
private static final Comparator<LocalizedNode> INSTANCE = new NodeWithContextComparator();
|
||||
private static final Comparator<LocalizedNode> REVERSE = INSTANCE.reversed();
|
||||
|
||||
public static Comparator<LocalizedNode> normal() {
|
||||
return INSTANCE;
|
||||
}
|
||||
public static Comparator<LocalizedNode> reverse() {
|
||||
return INSTANCE.reversed();
|
||||
}
|
||||
|
||||
private final Collator collator = Collator.getInstance(Locale.ENGLISH);
|
||||
private final LoadingCache<String, CollationKey> collationKeyCache = Caffeine.newBuilder().build(collator::getCollationKey);
|
||||
public static Comparator<LocalizedNode> reverse() {
|
||||
return REVERSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(LocalizedNode one, LocalizedNode two) {
|
||||
@ -97,27 +92,8 @@ public class NodeWithContextComparator implements Comparator<LocalizedNode> {
|
||||
return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1;
|
||||
}
|
||||
|
||||
return compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1;
|
||||
return CollationKeyCache.compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1;
|
||||
}
|
||||
|
||||
public int compareStrings(String o1, String o2) {
|
||||
if (o1.equals(o2)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
CollationKey o1c = collationKeyCache.get(o1);
|
||||
CollationKey o2c = collationKeyCache.get(o2);
|
||||
int i = o1c.compareTo(o2c);
|
||||
if (i != 0) {
|
||||
return i;
|
||||
}
|
||||
|
||||
// fallback to standard string comparison
|
||||
return o1.compareTo(o2);
|
||||
} catch (Exception e) {
|
||||
// ignored
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import lombok.Getter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
@ -1129,7 +1128,6 @@ public class SqlDao extends AbstractDao {
|
||||
}
|
||||
|
||||
private NodeModel deserializeNode(String permission, boolean value, String server, String world, long expiry, String contexts) {
|
||||
JsonObject context = gson.fromJson(contexts, JsonObject.class);
|
||||
return NodeModel.of(permission, value, server, world, expiry, ContextSetJsonSerializer.deserializeContextSet(context).makeImmutable());
|
||||
return NodeModel.of(permission, value, server, world, expiry, ContextSetJsonSerializer.deserializeContextSet(gson, contexts).makeImmutable());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.common.utils;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
|
||||
import java.text.CollationKey;
|
||||
import java.text.Collator;
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class CollationKeyCache implements Comparator<String> {
|
||||
private static final CollationKeyCache INSTANCE = new CollationKeyCache();
|
||||
|
||||
private static final Collator COLLATOR = Collator.getInstance(Locale.ENGLISH);
|
||||
|
||||
static {
|
||||
COLLATOR.setStrength(Collator.IDENTICAL);
|
||||
COLLATOR.setDecomposition(Collator.FULL_DECOMPOSITION);
|
||||
}
|
||||
|
||||
private static final LoadingCache<String, CollationKey> CACHE = Caffeine.newBuilder()
|
||||
.maximumSize(1000)
|
||||
.expireAfterAccess(5, TimeUnit.MINUTES)
|
||||
.build(COLLATOR::getCollationKey);
|
||||
|
||||
public static Comparator<String> comparator() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private CollationKeyCache() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
return compareStrings(o1, o2);
|
||||
}
|
||||
|
||||
public static int compareStrings(String o1, String o2) {
|
||||
//noinspection StringEquality
|
||||
if (o1 == o2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
CollationKey o1c = CACHE.get(o1);
|
||||
CollationKey o2c = CACHE.get(o2);
|
||||
|
||||
if (o1c != null && o2c != null) {
|
||||
int i = o1c.compareTo(o2c);
|
||||
if (i != 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to standard string comparison
|
||||
return o1.compareTo(o2);
|
||||
} catch (Exception e) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
// shrug
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ import com.google.gson.JsonObject;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.contexts.ContextSetComparator;
|
||||
import me.lucko.luckperms.common.contexts.ContextSetJsonSerializer;
|
||||
import me.lucko.luckperms.common.node.NodeWithContextComparator;
|
||||
import me.lucko.luckperms.common.utils.CollationKeyCache;
|
||||
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
|
||||
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
||||
import me.lucko.luckperms.sponge.service.model.SubjectReference;
|
||||
@ -195,7 +195,7 @@ public class SubjectStorageModel {
|
||||
|
||||
// sort alphabetically.
|
||||
List<Map.Entry<String, Boolean>> perms = new ArrayList<>(e.getValue().entrySet());
|
||||
perms.sort((o1, o2) -> NodeWithContextComparator.get().compareStrings(o1.getKey(), o2.getKey()));
|
||||
perms.sort(Map.Entry.comparingByKey(CollationKeyCache.comparator()));
|
||||
|
||||
for (Map.Entry<String, Boolean> ent : perms) {
|
||||
data.addProperty(ent.getKey(), ent.getValue());
|
||||
@ -219,7 +219,7 @@ public class SubjectStorageModel {
|
||||
|
||||
// sort alphabetically.
|
||||
List<Map.Entry<String, String>> opts = new ArrayList<>(e.getValue().entrySet());
|
||||
opts.sort((o1, o2) -> NodeWithContextComparator.get().compareStrings(o1.getKey(), o2.getKey()));
|
||||
opts.sort(Map.Entry.comparingByKey(CollationKeyCache.comparator()));
|
||||
|
||||
for (Map.Entry<String, String> ent : opts) {
|
||||
data.addProperty(ent.getKey(), ent.getValue());
|
||||
|
Loading…
Reference in New Issue
Block a user