Various performance optimizations

This commit is contained in:
Luck
2018-05-15 00:08:23 +01:00
Unverified
parent a0be1c7c48
commit 18f09f9862
72 changed files with 533 additions and 967 deletions
@@ -1,47 +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.common.calculators;
import com.google.common.collect.MapMaker;
import java.util.Collections;
import java.util.Set;
public abstract class AbstractCalculatorFactory implements PlatformCalculatorFactory {
private final Set<PermissionCalculator> calculators = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
protected PermissionCalculator registerCalculator(PermissionCalculator calculator) {
this.calculators.add(calculator);
return calculator;
}
@Override
public void invalidateAll() {
for (PermissionCalculator calculator : this.calculators) {
calculator.invalidateCache();
}
}
}
@@ -44,7 +44,6 @@ import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator;
import me.lucko.luckperms.common.node.factory.NodeFactory;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.utils.CollationKeyCache;
import me.lucko.luckperms.common.utils.DurationFormatter;
import me.lucko.luckperms.common.utils.Iterators;
import me.lucko.luckperms.common.utils.Predicates;
@@ -126,7 +125,7 @@ public class ParentInfo extends SharedSubCommand {
}
private static final Comparator<LocalizedNode> ALPHABETICAL_NODE_COMPARATOR = (o1, o2) -> {
int i = CollationKeyCache.compareStrings(o1.getGroupName(), o2.getGroupName());
int i = o1.getGroupName().compareTo(o2.getGroupName());
if (i != 0) {
return i;
}
@@ -44,7 +44,6 @@ import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator;
import me.lucko.luckperms.common.node.factory.NodeFactory;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.utils.CollationKeyCache;
import me.lucko.luckperms.common.utils.DurationFormatter;
import me.lucko.luckperms.common.utils.Iterators;
import me.lucko.luckperms.common.utils.Predicates;
@@ -129,7 +128,7 @@ public class PermissionInfo extends SharedSubCommand {
}
private static final Comparator<LocalizedNode> ALPHABETICAL_NODE_COMPARATOR = (o1, o2) -> {
int i = CollationKeyCache.compareStrings(o1.getPermission(), o2.getPermission());
int i = o1.getPermission().compareTo(o2.getPermission());
if (i != 0) {
return i;
}
@@ -26,6 +26,7 @@
package me.lucko.luckperms.common.commands.log;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.command.CommandResult;
import me.lucko.luckperms.common.command.abstraction.SubCommand;
@@ -54,7 +55,7 @@ public class LogNotify extends SubCommand<Log> {
return false;
}
Optional<? extends Node> ret = user.getOwnNodes().stream()
Optional<? extends Node> ret = user.enduringData().immutable().get(ContextSet.empty()).stream()
.filter(n -> n.getPermission().equalsIgnoreCase("luckperms.log.notify.ignoring"))
.findFirst();
@@ -74,7 +75,7 @@ public class LogNotify extends SubCommand<Log> {
user.setPermission(NodeFactory.make("luckperms.log.notify.ignoring"));
} else {
// remove the perm
user.removeIf(n -> n.getPermission().equalsIgnoreCase("luckperms.log.notify.ignoring"));
user.removeIf(ContextSet.empty(), n -> n.getPermission().equalsIgnoreCase("luckperms.log.notify.ignoring"));
}
plugin.getStorage().saveUser(user).join();
@@ -85,7 +85,7 @@ public class ExportCommand extends SingleCommand {
Exporter exporter = new Exporter(plugin, sender, path, includeUsers);
// Run the exporter in its own thread.
plugin.getBootstrap().getScheduler().doAsync(() -> {
plugin.getBootstrap().getScheduler().executeAsync(() -> {
try {
exporter.run();
} finally {
@@ -86,7 +86,7 @@ public class ImportCommand extends SingleCommand {
Importer importer = new Importer(plugin.getCommandManager(), sender, commands);
// Run the importer in its own thread.
plugin.getBootstrap().getScheduler().doAsync(() -> {
plugin.getBootstrap().getScheduler().executeAsync(() -> {
try {
importer.run();
} finally {
@@ -25,34 +25,31 @@
package me.lucko.luckperms.common.config;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import me.lucko.luckperms.common.api.delegates.misc.ApiConfiguration;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
import me.lucko.luckperms.common.config.keys.EnduringKey;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import java.util.Map;
/**
* An abstract implementation of {@link LuckPermsConfiguration}, backed by a cache.
* An abstract implementation of {@link LuckPermsConfiguration}.
*
* <p>Values are loaded into memory on init.</p>
*/
public class AbstractConfiguration implements LuckPermsConfiguration, CacheLoader<ConfigKey<?>, Optional<Object>> {
public class AbstractConfiguration implements LuckPermsConfiguration {
// the loading cache for config keys --> their value
// the value is wrapped in an optional as null values don't get cached.
private final LoadingCache<ConfigKey<?>, Optional<Object>> cache = Caffeine.newBuilder().build(this);
/**
* The configurations loaded values.
*
* <p>The value corresponding to each key is stored at the index defined
* by {@link ConfigKey#ordinal()}.</p>
*/
private Object[] values = null;
// the plugin instance
private final LuckPermsPlugin plugin;
// the adapter used to read values
private final ConfigurationAdapter adapter;
// the api delegate
private final ApiConfiguration delegate = new ApiConfiguration(this);
// the contextsfile handler
@@ -61,6 +58,52 @@ public class AbstractConfiguration implements LuckPermsConfiguration, CacheLoade
public AbstractConfiguration(LuckPermsPlugin plugin, ConfigurationAdapter adapter) {
this.plugin = plugin;
this.adapter = adapter;
load();
}
@SuppressWarnings("unchecked")
@Override
public <T> T get(ConfigKey<T> key) {
return (T) this.values[key.ordinal()];
}
@Override
public synchronized void load() {
// get the map of all keys
Map<String, ConfigKey<?>> keys = ConfigKeys.getAllKeys();
// if this is a reload operation
boolean reload = true;
// if values are null, must be loading for the first time
if (this.values == null) {
this.values = new Object[keys.size()];
reload = false;
}
// load a value for each key.
for (ConfigKey<?> key : keys.values()) {
// don't reload enduring keys.
if (reload && key instanceof EnduringKey) {
continue;
}
// load the value for the key
Object value = key.get(this.adapter);
this.values[key.ordinal()] = value;
}
// load the contexts file
this.contextsFile.load();
}
@Override
public void reload() {
this.adapter.reload();
load();
getPlugin().getEventFactory().handleConfigReload();
}
public ConfigurationAdapter getAdapter() {
@@ -81,36 +124,4 @@ public class AbstractConfiguration implements LuckPermsConfiguration, CacheLoade
public ContextsFile getContextsFile() {
return this.contextsFile;
}
@SuppressWarnings("unchecked")
@Override
public <T> T get(ConfigKey<T> key) {
Optional<Object> ret = this.cache.get(key);
if (ret == null) {
return null;
}
return (T) ret.orElse(null);
}
@Override
public void loadAll() {
ConfigKeys.getAllKeys().values().forEach(this.cache::get);
this.contextsFile.load();
}
@Override
public void reload() {
this.adapter.reload();
Set<ConfigKey<?>> toInvalidate = this.cache.asMap().keySet().stream().filter(k -> !(k instanceof EnduringKey)).collect(Collectors.toSet());
this.cache.invalidateAll(toInvalidate);
loadAll();
getPlugin().getEventFactory().handleConfigReload();
}
@Override
public Optional<Object> load(@Nonnull ConfigKey<?> key) {
return Optional.ofNullable(key.get(this.adapter));
}
}
@@ -23,17 +23,17 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.calculators;
package me.lucko.luckperms.common.config;
/**
* Extension of {@link CalculatorFactory} which keeps a record of produced
* calculators and provides a means to invalidate them all in bulk.
*/
public interface PlatformCalculatorFactory extends CalculatorFactory {
public abstract class BaseConfigKey<T> implements ConfigKey<T> {
int ordinal = -1;
/**
* Invalidates all calculators build by this factory
*/
void invalidateAll();
protected BaseConfigKey() {
}
@Override
public int ordinal() {
return this.ordinal;
}
}
@@ -34,6 +34,16 @@ import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
*/
public interface ConfigKey<T> {
/**
* Gets the position of this key within the {@link ConfigKeys} enum.
*
* <p>This is lazily set when the configuration is loaded for the first time, during the
* execution of {@link ConfigKeys#getAllKeys()}.</p>
*
* @return the position
*/
int ordinal();
/**
* Resolves and returns the value mapped to this key using the given config instance.
*
@@ -33,8 +33,8 @@ import me.lucko.luckperms.api.LookupSetting;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.metastacking.MetaStackDefinition;
import me.lucko.luckperms.common.assignments.AssignmentRule;
import me.lucko.luckperms.common.config.keys.AbstractKey;
import me.lucko.luckperms.common.config.keys.BooleanKey;
import me.lucko.luckperms.common.config.keys.CustomKey;
import me.lucko.luckperms.common.config.keys.EnduringKey;
import me.lucko.luckperms.common.config.keys.LowercaseStringKey;
import me.lucko.luckperms.common.config.keys.MapKey;
@@ -64,13 +64,16 @@ import java.util.function.Function;
/**
* All of the {@link ConfigKey}s used by LuckPerms.
*
* <p>The {@link #getAllKeys()} method and associated behaviour allows this class to behave
* a bit like an enum with generics.</p>
*/
public class ConfigKeys {
public final class ConfigKeys {
/**
* The name of the server
*/
public static final ConfigKey<String> SERVER = AbstractKey.of(c -> {
public static final ConfigKey<String> SERVER = CustomKey.of(c -> {
if (c.getBoolean("use-server-properties-name", false)) {
String serverName = c.getPlugin().getBootstrap().getServerName();
if (serverName != null && !serverName.equals("Unknown Server")) {
@@ -84,7 +87,7 @@ public class ConfigKeys {
/**
* How many minutes to wait between syncs. A value <= 0 will disable syncing.
*/
public static final ConfigKey<Integer> SYNC_TIME = EnduringKey.wrap(AbstractKey.of(c -> {
public static final ConfigKey<Integer> SYNC_TIME = EnduringKey.wrap(CustomKey.of(c -> {
int val = c.getInt("sync-minutes", -1);
if (val == -1) {
val = c.getInt("data.sync-minutes", -1);
@@ -95,7 +98,7 @@ public class ConfigKeys {
/**
* The lookup settings for contexts (care should be taken to not mutate this method)
*/
public static final ConfigKey<EnumSet<LookupSetting>> LOOKUP_SETTINGS = AbstractKey.of(c -> {
public static final ConfigKey<EnumSet<LookupSetting>> LOOKUP_SETTINGS = CustomKey.of(c -> {
return EnumSet.copyOf(Contexts.of(
ContextSet.empty(),
c.getBoolean("include-global", true),
@@ -130,7 +133,7 @@ public class ConfigKeys {
/**
* Controls how temporary add commands should behave
*/
public static final ConfigKey<TemporaryModifier> TEMPORARY_ADD_BEHAVIOUR = AbstractKey.of(c -> {
public static final ConfigKey<TemporaryModifier> TEMPORARY_ADD_BEHAVIOUR = CustomKey.of(c -> {
String option = c.getString("temporary-add-behaviour", "deny").toLowerCase();
if (!option.equals("deny") && !option.equals("replace") && !option.equals("accumulate")) {
option = "deny";
@@ -142,7 +145,7 @@ public class ConfigKeys {
/**
* How primary groups should be calculated.
*/
public static final ConfigKey<String> PRIMARY_GROUP_CALCULATION_METHOD = EnduringKey.wrap(AbstractKey.of(c -> {
public static final ConfigKey<String> PRIMARY_GROUP_CALCULATION_METHOD = EnduringKey.wrap(CustomKey.of(c -> {
String option = c.getString("primary-group-calculation", "stored").toLowerCase();
if (!option.equals("stored") && !option.equals("parents-by-weight") && !option.equals("all-parents-by-weight")) {
option = "stored";
@@ -154,7 +157,7 @@ public class ConfigKeys {
/**
* A function to create primary group holder instances based upon the {@link #PRIMARY_GROUP_CALCULATION_METHOD} setting.
*/
public static final ConfigKey<Function<User, PrimaryGroupHolder>> PRIMARY_GROUP_CALCULATION = EnduringKey.wrap(AbstractKey.of(c -> {
public static final ConfigKey<Function<User, PrimaryGroupHolder>> PRIMARY_GROUP_CALCULATION = EnduringKey.wrap(CustomKey.of(c -> {
String option = PRIMARY_GROUP_CALCULATION_METHOD.get(c);
switch (option) {
case "stored":
@@ -245,7 +248,7 @@ public class ConfigKeys {
/**
* The algorithm LuckPerms should use when traversing the "inheritance tree"
*/
public static final ConfigKey<TraversalAlgorithm> INHERITANCE_TRAVERSAL_ALGORITHM = AbstractKey.of(c -> {
public static final ConfigKey<TraversalAlgorithm> INHERITANCE_TRAVERSAL_ALGORITHM = CustomKey.of(c -> {
String value = c.getString("inheritance-traversal-algorithm", "depth-first-pre-order");
switch (value.toLowerCase()) {
case "breadth-first":
@@ -260,7 +263,7 @@ public class ConfigKeys {
/**
* The configured group weightings
*/
public static final ConfigKey<Map<String, Integer>> GROUP_WEIGHTS = AbstractKey.of(c -> {
public static final ConfigKey<Map<String, Integer>> GROUP_WEIGHTS = CustomKey.of(c -> {
return c.getMap("group-weight", ImmutableMap.of()).entrySet().stream().collect(ImmutableCollectors.toMap(
e -> e.getKey().toLowerCase(),
e -> {
@@ -276,7 +279,7 @@ public class ConfigKeys {
/**
* Creates a new prefix MetaStack element based upon the configured values.
*/
public static final ConfigKey<MetaStackDefinition> PREFIX_FORMATTING_OPTIONS = AbstractKey.of(l -> {
public static final ConfigKey<MetaStackDefinition> PREFIX_FORMATTING_OPTIONS = CustomKey.of(l -> {
List<String> format = l.getList("meta-formatting.prefix.format", new ArrayList<>());
if (format.isEmpty()) {
format.add("highest");
@@ -291,7 +294,7 @@ public class ConfigKeys {
/**
* Creates a new suffix MetaStack element based upon the configured values.
*/
public static final ConfigKey<MetaStackDefinition> SUFFIX_FORMATTING_OPTIONS = AbstractKey.of(l -> {
public static final ConfigKey<MetaStackDefinition> SUFFIX_FORMATTING_OPTIONS = CustomKey.of(l -> {
List<String> format = l.getList("meta-formatting.suffix.format", new ArrayList<>());
if (format.isEmpty()) {
format.add("highest");
@@ -316,7 +319,7 @@ public class ConfigKeys {
/**
* If server operators should be enabled. Only used by the Bukkit platform.
*/
public static final ConfigKey<Boolean> OPS_ENABLED = EnduringKey.wrap(AbstractKey.of(c -> !AUTO_OP.get(c) && c.getBoolean("enable-ops", true)));
public static final ConfigKey<Boolean> OPS_ENABLED = EnduringKey.wrap(CustomKey.of(c -> !AUTO_OP.get(c) && c.getBoolean("enable-ops", true)));
/**
* If server operators should be able to use LuckPerms commands by default. Only used by the Bukkit platform.
@@ -331,7 +334,7 @@ public class ConfigKeys {
/**
* The name of the server to use for Vault.
*/
public static final ConfigKey<String> VAULT_SERVER = AbstractKey.of(c -> {
public static final ConfigKey<String> VAULT_SERVER = CustomKey.of(c -> {
// default to true for backwards compatibility
if (USE_VAULT_SERVER.get(c)) {
return c.getString("vault-server", "global").toLowerCase();
@@ -358,7 +361,7 @@ public class ConfigKeys {
/**
* The world rewrites map
*/
public static final ConfigKey<Map<String, String>> WORLD_REWRITES = AbstractKey.of(c -> {
public static final ConfigKey<Map<String, String>> WORLD_REWRITES = CustomKey.of(c -> {
return c.getMap("world-rewrite", ImmutableMap.of()).entrySet().stream()
.collect(ImmutableCollectors.toMap(
e -> e.getKey().toLowerCase(),
@@ -374,7 +377,7 @@ public class ConfigKeys {
/**
* The default assignments being applied by the plugin
*/
public static final ConfigKey<List<AssignmentRule>> DEFAULT_ASSIGNMENTS = AbstractKey.of(c -> {
public static final ConfigKey<List<AssignmentRule>> DEFAULT_ASSIGNMENTS = CustomKey.of(c -> {
return c.getObjectList("default-assignments", ImmutableList.of()).stream().map(name -> {
String hasTrue = c.getString("default-assignments." + name + ".if.has-true", null);
String hasFalse = c.getString("default-assignments." + name + ".if.has-false", null);
@@ -389,7 +392,7 @@ public class ConfigKeys {
/**
* The database settings, username, password, etc for use by any database
*/
public static final ConfigKey<StorageCredentials> DATABASE_VALUES = EnduringKey.wrap(AbstractKey.of(c -> {
public static final ConfigKey<StorageCredentials> DATABASE_VALUES = EnduringKey.wrap(CustomKey.of(c -> {
int maxPoolSize = c.getInt("data.pool-settings.maximum-pool-size", c.getInt("data.pool-size", 10));
int minIdle = c.getInt("data.pool-settings.minimum-idle", maxPoolSize);
int maxLifetime = c.getInt("data.pool-settings.maximum-lifetime", 1800000);
@@ -438,7 +441,7 @@ public class ConfigKeys {
/**
* The options for split storage
*/
public static final ConfigKey<Map<SplitStorageType, String>> SPLIT_STORAGE_OPTIONS = EnduringKey.wrap(AbstractKey.of(c -> {
public static final ConfigKey<Map<SplitStorageType, String>> SPLIT_STORAGE_OPTIONS = EnduringKey.wrap(CustomKey.of(c -> {
EnumMap<SplitStorageType, String> map = new EnumMap<>(SplitStorageType.class);
map.put(SplitStorageType.USER, c.getString("split-storage.methods.user", "h2").toLowerCase());
map.put(SplitStorageType.GROUP, c.getString("split-storage.methods.group", "h2").toLowerCase());
@@ -511,15 +514,25 @@ public class ConfigKeys {
try {
Field[] values = ConfigKeys.class.getFields();
int counter = 0;
for (Field f : values) {
// ignore non-static fields
if (!Modifier.isStatic(f.getModifiers())) {
continue;
}
Object val = f.get(null);
if (val instanceof ConfigKey<?>) {
keys.put(f.getName(), (ConfigKey<?>) val);
// ignore fields that aren't configkeys
if (!ConfigKey.class.equals(f.getType())) {
continue;
}
// get the key instance
BaseConfigKey<?> key = (BaseConfigKey<?>) f.get(null);
// set the ordinal value of the key.
key.ordinal = counter++;
// add the key to the return map
keys.put(f.getName(), key);
}
} catch (Exception e) {
e.printStackTrace();
@@ -60,9 +60,9 @@ public interface LuckPermsConfiguration {
void reload();
/**
* Pre-loads all configuration keys into the cache.
* Loads all configuration values.
*/
void loadAll();
void load();
/**
* Gets the value of a given context key.
@@ -25,10 +25,10 @@
package me.lucko.luckperms.common.config.keys;
import me.lucko.luckperms.common.config.ConfigKey;
import me.lucko.luckperms.common.config.BaseConfigKey;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
public class BooleanKey implements ConfigKey<Boolean> {
public class BooleanKey extends BaseConfigKey<Boolean> {
public static BooleanKey of(String path, boolean def) {
return new BooleanKey(path, def);
}
@@ -25,19 +25,19 @@
package me.lucko.luckperms.common.config.keys;
import me.lucko.luckperms.common.config.ConfigKey;
import me.lucko.luckperms.common.config.BaseConfigKey;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
import java.util.function.Function;
public class AbstractKey<T> implements ConfigKey<T> {
public static <T> AbstractKey<T> of(Function<ConfigurationAdapter, T> function) {
return new AbstractKey<>(function);
public class CustomKey<T> extends BaseConfigKey<T> {
public static <T> CustomKey<T> of(Function<ConfigurationAdapter, T> function) {
return new CustomKey<>(function);
}
private final Function<ConfigurationAdapter, T> function;
private AbstractKey(Function<ConfigurationAdapter, T> function) {
private CustomKey(Function<ConfigurationAdapter, T> function) {
this.function = function;
}
@@ -25,6 +25,7 @@
package me.lucko.luckperms.common.config.keys;
import me.lucko.luckperms.common.config.BaseConfigKey;
import me.lucko.luckperms.common.config.ConfigKey;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
@@ -32,7 +33,7 @@ import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
* Wrapper class to mark a config key as enduring (doesn't change in the event of a reload)
* @param <T>
*/
public class EnduringKey<T> implements ConfigKey<T> {
public class EnduringKey<T> extends BaseConfigKey<T> {
public static <T> EnduringKey<T> wrap(ConfigKey<T> delegate) {
return new EnduringKey<>(delegate);
@@ -25,10 +25,10 @@
package me.lucko.luckperms.common.config.keys;
import me.lucko.luckperms.common.config.ConfigKey;
import me.lucko.luckperms.common.config.BaseConfigKey;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
public class IntegerKey implements ConfigKey<Integer> {
public class IntegerKey extends BaseConfigKey<Integer> {
public static IntegerKey of(String path, int def) {
return new IntegerKey(path, def);
}
@@ -25,10 +25,10 @@
package me.lucko.luckperms.common.config.keys;
import me.lucko.luckperms.common.config.ConfigKey;
import me.lucko.luckperms.common.config.BaseConfigKey;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
public class LowercaseStringKey implements ConfigKey<String> {
public class LowercaseStringKey extends BaseConfigKey<String> {
public static LowercaseStringKey of(String path, String def) {
return new LowercaseStringKey(path, def);
}
@@ -27,12 +27,12 @@ package me.lucko.luckperms.common.config.keys;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.common.config.ConfigKey;
import me.lucko.luckperms.common.config.BaseConfigKey;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
import java.util.Map;
public class MapKey implements ConfigKey<Map<String, String>> {
public class MapKey extends BaseConfigKey<Map<String, String>> {
public static MapKey of(String path) {
return new MapKey(path);
}
@@ -25,10 +25,10 @@
package me.lucko.luckperms.common.config.keys;
import me.lucko.luckperms.common.config.ConfigKey;
import me.lucko.luckperms.common.config.BaseConfigKey;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
public class StringKey implements ConfigKey<String> {
public class StringKey extends BaseConfigKey<String> {
public static StringKey of(String path, String def) {
return new StringKey(path, def);
}
@@ -221,6 +221,6 @@ public abstract class AbstractEventBus<P> implements EventBus, AutoCloseable {
if (event instanceof Cancellable) {
throw new IllegalArgumentException("cannot call Cancellable event async");
}
this.plugin.getBootstrap().getScheduler().doAsync(() -> fireEvent(event));
this.plugin.getBootstrap().getScheduler().executeAsync(() -> fireEvent(event));
}
}
@@ -25,9 +25,6 @@
package me.lucko.luckperms.common.inheritance;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LookupSetting;
import me.lucko.luckperms.api.Node;
@@ -38,7 +35,6 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
/**
* Provides {@link InheritanceGraph}s.
@@ -51,17 +47,15 @@ public class InheritanceHandler {
*/
private final InheritanceGraph nonContextualGraph;
/**
* Cache of contextual inheritance graph instances
*/
private final LoadingCache<Contexts, InheritanceGraph> contextualGraphs;
// some cached contextual graphs for common Contexts
private final InheritanceGraph allowAllContextualGraph;
private final InheritanceGraph globalContextualGraph;
public InheritanceHandler(LuckPermsPlugin plugin) {
this.plugin = plugin;
this.nonContextualGraph = new NonContextualGraph(plugin);
this.contextualGraphs = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(key -> new ContextualGraph(this.plugin, key));
this.allowAllContextualGraph = new ContextualGraph(plugin, Contexts.allowAll());
this.globalContextualGraph = new ContextualGraph(plugin, Contexts.global());
}
public InheritanceGraph getGraph() {
@@ -69,7 +63,14 @@ public class InheritanceHandler {
}
public InheritanceGraph getGraph(Contexts contexts) {
return this.contextualGraphs.get(contexts);
if (contexts == Contexts.allowAll()) {
return this.allowAllContextualGraph;
}
if (contexts == Contexts.global()) {
return this.globalContextualGraph;
}
return new ContextualGraph(this.plugin, contexts);
}
private static final class NonContextualGraph implements InheritanceGraph {
@@ -37,6 +37,7 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public abstract class AbstractUserManager<T extends User> extends AbstractManager<UserIdentifier, User, T> implements UserManager<T> {
@@ -46,7 +47,7 @@ public abstract class AbstractUserManager<T extends User> extends AbstractManage
public AbstractUserManager(LuckPermsPlugin plugin, UserHousekeeper.TimeoutSettings timeoutSettings) {
this.plugin = plugin;
this.housekeeper = new UserHousekeeper(plugin, this, timeoutSettings);
this.plugin.getBootstrap().getScheduler().asyncRepeating(this.housekeeper, 200L); // every 10 seconds
this.plugin.getBootstrap().getScheduler().asyncRepeating(this.housekeeper, 10, TimeUnit.SECONDS);
}
@Override
@@ -102,7 +102,7 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
@Override
public void pushUpdate() {
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
UUID requestId = generatePingId();
this.plugin.getLogger().info("[" + getName() + " Messaging] Sending ping with id: " + requestId);
this.messenger.sendOutgoingMessage(new UpdateMessageImpl(requestId));
@@ -111,7 +111,7 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
@Override
public void pushUserUpdate(User user) {
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
UUID requestId = generatePingId();
this.plugin.getLogger().info("[" + getName() + " Messaging] Sending user ping for '" + user.getFriendlyName() + "' with id: " + requestId);
this.messenger.sendOutgoingMessage(new UserUpdateMessageImpl(requestId, user.getUuid()));
@@ -120,7 +120,7 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
@Override
public void pushLog(LogEntry logEntry) {
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
UUID requestId = generatePingId();
if (this.plugin.getEventFactory().handleLogNetworkPublish(!this.plugin.getConfiguration().get(ConfigKeys.PUSH_LOG_ENTRIES), requestId, logEntry)) {
@@ -65,7 +65,7 @@ public class RedisMessenger implements Messenger {
this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, 0, password);
}
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
this.sub = new LPSub(this);
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.subscribe(this.sub, CHANNEL);
@@ -27,12 +27,13 @@ package me.lucko.luckperms.common.messaging.sql;
import me.lucko.luckperms.api.messenger.IncomingMessageConsumer;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import me.lucko.luckperms.common.storage.dao.sql.SqlDao;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
public class SqlMessenger extends AbstractSqlMessenger {
private final LuckPermsPlugin plugin;
@@ -57,8 +58,8 @@ public class SqlMessenger extends AbstractSqlMessenger {
// schedule poll tasks
SchedulerAdapter scheduler = this.plugin.getBootstrap().getScheduler();
this.pollTask = scheduler.asyncRepeating(this::pollMessages, 20L);
this.housekeepingTask = scheduler.asyncRepeating(this::runHousekeeping, 20L * 30);
this.pollTask = scheduler.asyncRepeating(this::pollMessages, 1, TimeUnit.SECONDS);
this.housekeepingTask = scheduler.asyncRepeating(this::runHousekeeping, 30, TimeUnit.SECONDS);
}
@Override
@@ -26,7 +26,6 @@
package me.lucko.luckperms.common.node.comparator;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.common.utils.CollationKeyCache;
import java.util.Comparator;
@@ -65,6 +64,6 @@ public class NodeComparator implements Comparator<Node> {
return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1;
}
return CollationKeyCache.compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1;
return o1.getPermission().compareTo(o2.getPermission()) > 0 ? -1 : 1;
}
}
@@ -26,7 +26,7 @@
package me.lucko.luckperms.common.node.comparator;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.common.utils.CollationKeyCache;
import me.lucko.luckperms.common.contexts.ContextSetComparator;
import java.util.Comparator;
@@ -58,35 +58,16 @@ public class NodeWithContextComparator implements Comparator<Node> {
return o1.isOverride() ? 1 : -1;
}
if (o1.isServerSpecific() != o2.isServerSpecific()) {
return o1.isServerSpecific() ? 1 : -1;
int i = ContextSetComparator.normal().compare(
o1.getFullContexts().makeImmutable(),
o2.getFullContexts().makeImmutable()
);
if (i != 0) {
return i;
}
if (o1.isWorldSpecific() != o2.isWorldSpecific()) {
return o1.isWorldSpecific() ? 1 : -1;
}
if (o1.getContexts().size() != o2.getContexts().size()) {
return o1.getContexts().size() > o2.getContexts().size() ? 1 : -1;
}
if (o1.isTemporary() != o2.isTemporary()) {
return o1.isTemporary() ? 1 : -1;
}
if (o1.isWildcard() != o2.isWildcard()) {
return o1.isWildcard() ? 1 : -1;
}
if (o1.isTemporary()) {
return o1.getSecondsTilExpiry() < o2.getSecondsTilExpiry() ? 1 : -1;
}
if (o1.isWildcard()) {
return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1;
}
return CollationKeyCache.compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1;
return NodeComparator.normal().compare(o1, o2);
}
}
@@ -42,7 +42,7 @@ import javax.annotation.Nonnull;
public abstract class ForwardingNode implements Node {
protected abstract Node delegate();
public abstract Node delegate();
@Override
public int hashCode() {
@@ -52,7 +52,7 @@ public final class ImmutableLocalizedNode extends ForwardingNode implements Loca
}
@Override
protected Node delegate() {
public Node delegate() {
return this.node;
}
@@ -85,7 +85,6 @@ public final class ImmutableNode implements Node {
// cached state
private final Optional<String> optServer;
private final Optional<String> optWorld;
@@ -48,7 +48,7 @@ public final class ImmutableTransientNode<O> extends ForwardingNode implements N
}
@Override
protected Node delegate() {
public Node delegate() {
return this.node;
}
@@ -31,7 +31,7 @@ import me.lucko.luckperms.common.api.ApiRegistrationUtil;
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
import me.lucko.luckperms.common.buffers.BufferedRequest;
import me.lucko.luckperms.common.buffers.UpdateTaskBuffer;
import me.lucko.luckperms.common.calculators.PlatformCalculatorFactory;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.command.utils.MessageUtils;
import me.lucko.luckperms.common.config.AbstractConfiguration;
import me.lucko.luckperms.common.config.ConfigKeys;
@@ -61,6 +61,7 @@ import me.lucko.luckperms.common.verbose.VerboseHandler;
import java.io.IOException;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
@@ -79,7 +80,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
private InternalMessagingService messagingService = null;
private BufferedRequest<Void> updateTaskBuffer;
private InheritanceHandler inheritanceHandler;
private PlatformCalculatorFactory calculatorFactory;
private CalculatorFactory calculatorFactory;
private LuckPermsApiProvider apiProvider;
private EventFactory eventFactory;
@@ -101,14 +102,13 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
displayBanner(getConsoleSender());
// load some utilities early
this.verboseHandler = new VerboseHandler();
this.permissionRegistry = new PermissionRegistry();
this.verboseHandler = new VerboseHandler(getBootstrap().getScheduler());
this.permissionRegistry = new PermissionRegistry(getBootstrap().getScheduler());
this.logDispatcher = new LogDispatcher(this);
// load configuration
getLogger().info("Loading configuration...");
this.configuration = new AbstractConfiguration(this, provideConfigurationAdapter());
this.configuration.loadAll();
// load locale
this.localeManager = new SimpleLocaleManager();
@@ -168,10 +168,9 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
// schedule update tasks
int mins = getConfiguration().get(ConfigKeys.SYNC_TIME);
if (mins > 0) {
long ticks = mins * 60 * 20;
getBootstrap().getScheduler().asyncRepeating(() -> this.updateTaskBuffer.request(), ticks);
getBootstrap().getScheduler().asyncRepeating(() -> this.updateTaskBuffer.request(), mins, TimeUnit.MINUTES);
}
getBootstrap().getScheduler().asyncLater(() -> this.updateTaskBuffer.request(), 40L);
getBootstrap().getScheduler().asyncLater(() -> this.updateTaskBuffer.request(), 2, TimeUnit.SECONDS);
// run an update instantly.
getLogger().info("Performing initial data load...");
@@ -191,9 +190,6 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
}
public final void disable() {
// perform initial disable tasks
performEarlyDisableTasks();
// shutdown permission vault and verbose handler tasks
this.permissionRegistry.stop();
this.verboseHandler.stop();
@@ -233,7 +229,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
protected abstract MessagingFactory<?> provideMessagingFactory();
protected abstract void registerCommands();
protected abstract void setupManagers();
protected abstract PlatformCalculatorFactory provideCalculatorFactory();
protected abstract CalculatorFactory provideCalculatorFactory();
protected abstract void setupContextManager();
protected abstract void setupPlatformHooks();
protected abstract AbstractEventBus provideEventBus(LuckPermsApiProvider apiProvider);
@@ -241,7 +237,6 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
protected abstract void registerHousekeepingTasks();
protected abstract void performFinalSetup();
protected void performEarlyDisableTasks() {}
protected void removePlatformHooks() {}
@Override
@@ -312,7 +307,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
}
@Override
public PlatformCalculatorFactory getCalculatorFactory() {
public CalculatorFactory getCalculatorFactory() {
return this.calculatorFactory;
}
@@ -27,7 +27,7 @@ package me.lucko.luckperms.common.plugin.bootstrap;
import me.lucko.luckperms.api.platform.PlatformType;
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import java.io.InputStream;
import java.nio.file.Path;
@@ -0,0 +1,98 @@
/*
* 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.plugin.scheduler;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Abstract implementation of {@link SchedulerAdapter} using a {@link ScheduledExecutorService}.
*/
public abstract class AbstractJavaScheduler implements SchedulerAdapter {
private final ScheduledExecutorService asyncExecutor = new AsyncExecutor();
@Override
public Executor async() {
return this.asyncExecutor;
}
@Override
public SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit) {
ScheduledFuture<?> future = this.asyncExecutor.schedule(new WrappedRunnable(task), delay, unit);
return () -> future.cancel(false);
}
@Override
public SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit) {
ScheduledFuture<?> future = this.asyncExecutor.scheduleAtFixedRate(new WrappedRunnable(task), interval, interval, unit);
return () -> future.cancel(false);
}
@Override
public void shutdown() {
this.asyncExecutor.shutdown();
try {
this.asyncExecutor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static final class WrappedRunnable implements Runnable {
private final Runnable delegate;
WrappedRunnable(Runnable delegate) {
this.delegate = delegate;
}
@Override
public void run() {
try {
this.delegate.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static final class AsyncExecutor extends ScheduledThreadPoolExecutor {
AsyncExecutor() {
super(4, new ThreadFactoryBuilder().setNameFormat("luckperms-%d").build());
}
@Override
public void execute(Runnable command) {
super.execute(new WrappedRunnable(command));
}
}
}
@@ -23,9 +23,12 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.plugin;
package me.lucko.luckperms.common.plugin.scheduler;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
* A scheduler for running tasks using the systems provided by the platform
@@ -39,10 +42,6 @@ public interface SchedulerAdapter {
*/
Executor async();
default Executor platformAsync() {
return async();
}
/**
* Gets a sync executor instance
*
@@ -51,46 +50,42 @@ public interface SchedulerAdapter {
Executor sync();
/**
* Executes a runnable async
* Executes a task async
*
* @param runnable the runnable
* @param task the task
*/
void doAsync(Runnable runnable);
default void executeAsync(Runnable task) {
async().execute(task);
}
/**
* Executes a runnable sync
* Executes a task sync
*
* @param runnable the runnable
* @param task the task
*/
void doSync(Runnable runnable);
default void executeSync(Runnable task) {
sync().execute(task);
}
/**
* Runs a runnable repeatedly until the plugin disables. Will wait for the interval before the first iteration of the task is ran.
* @param runnable the runnable
* @param intervalTicks the interval in ticks.
* Executes the given task with a delay.
*
* @param task the task
* @param delay the delay
* @param unit the unit of delay
* @return the resultant task instance
*/
SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks);
SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit);
/**
* Runs a runnable repeatedly until the plugin disables. Will wait for the interval before the first iteration of the task is ran.
* @param runnable the runnable
* @param intervalTicks the interval in ticks.
* Executes the given task repeatedly at a given interval.
*
* @param task the task
* @param interval the interval
* @param unit the unit of interval
* @return the resultant task instance
*/
SchedulerTask syncRepeating(Runnable runnable, long intervalTicks);
/**
* Runs a runnable with a delay
* @param runnable the runnable
* @param delayTicks the delay in ticks
*/
SchedulerTask asyncLater(Runnable runnable, long delayTicks);
/**
* Runs a runnable with a delay
* @param runnable the runnable
* @param delayTicks the delay in ticks
*/
SchedulerTask syncLater(Runnable runnable, long delayTicks);
SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit);
/**
* Shuts down this executor instance
@@ -61,8 +61,8 @@ public class FileWatcher {
this.basePath = basePath;
this.watchService = basePath.getFileSystem().newWatchService();
plugin.getBootstrap().getScheduler().asyncLater(this::initLocations, 25L);
plugin.getBootstrap().getScheduler().asyncRepeating(this::tick, 10L);
plugin.getBootstrap().getScheduler().asyncLater(this::initLocations, 5, TimeUnit.SECONDS);
plugin.getBootstrap().getScheduler().asyncRepeating(this::tick, 1, TimeUnit.SECONDS);
}
public WatchedLocation getWatcher(Path path) {
@@ -28,6 +28,7 @@ package me.lucko.luckperms.common.treeview;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import me.lucko.luckperms.common.utils.RepeatingTask;
import java.util.Collection;
@@ -55,8 +56,8 @@ public class PermissionRegistry extends RepeatingTask {
// a queue of permission strings to be processed by the tree
private final Queue<String> queue;
public PermissionRegistry() {
super(1000, TimeUnit.MILLISECONDS, "luckperms-permission-vault");
public PermissionRegistry(SchedulerAdapter scheduler) {
super(scheduler, 1, TimeUnit.SECONDS);
this.rootNode = new TreeNode();
this.knownPermissions = ConcurrentHashMap.newKeySet(3000);
this.queue = new ConcurrentLinkedQueue<>();
@@ -1,88 +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.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;
static {
COLLATOR = Collator.getInstance(Locale.ENGLISH);
COLLATOR.setStrength(Collator.IDENTICAL);
COLLATOR.setDecomposition(Collator.FULL_DECOMPOSITION);
}
private static final LoadingCache<String, CollationKey> CACHE = Caffeine.newBuilder()
.maximumSize(5000)
.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;
}
}
} catch (Exception e) {
// ignored
}
// fallback to standard string comparison
return o1.compareTo(o2);
}
}
@@ -25,8 +25,8 @@
package me.lucko.luckperms.common.utils;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ForwardingSet;
import java.util.Collection;
@@ -36,22 +36,22 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
/**
* A bad expiring set implementation using Caffeine caches
* A simple expiring set implementation using Caffeine caches
*
* @param <E> element type
*/
public class ExpiringSet<E> extends ForwardingSet<E> {
private final LoadingCache<E, Boolean> cache;
private final Cache<E, Boolean> cache;
private final Set<E> setView;
public ExpiringSet(long duration, TimeUnit unit) {
this.cache = Caffeine.newBuilder().expireAfterAccess(duration, unit).build(key -> Boolean.TRUE);
this.cache = Caffeine.newBuilder().expireAfterAccess(duration, unit).build();
this.setView = this.cache.asMap().keySet();
}
@Override
public boolean add(E element) {
this.cache.get(element); // simply requesting the element from the cache is sufficient.
this.cache.put(element, Boolean.TRUE);
// we don't care about the return value
return true;
@@ -69,12 +69,12 @@ public final class PatternCache {
private final Pattern instance;
private final PatternSyntaxException ex;
public CachedPattern(Pattern instance) {
CachedPattern(Pattern instance) {
this.instance = instance;
this.ex = null;
}
public CachedPattern(PatternSyntaxException ex) {
CachedPattern(PatternSyntaxException ex) {
this.instance = null;
this.ex = ex;
}
@@ -25,20 +25,16 @@
package me.lucko.luckperms.common.utils;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public abstract class RepeatingTask {
private final SchedulerTask task;
// the executor thread
private final ScheduledExecutorService executor;
protected RepeatingTask(long time, TimeUnit unit, String nameFormat) {
this.executor = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat(nameFormat).build());
this.executor.scheduleAtFixedRate(this::run, time, time, unit);
protected RepeatingTask(SchedulerAdapter scheduler, long time, TimeUnit unit) {
this.task = scheduler.asyncRepeating(this::run, time, unit);
}
private void run() {
@@ -52,11 +48,6 @@ public abstract class RepeatingTask {
protected abstract void tick();
public void stop() {
this.executor.shutdown();
try {
this.executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.task.cancel();
}
}
@@ -27,6 +27,7 @@ package me.lucko.luckperms.common.verbose;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.utils.RepeatingTask;
@@ -51,8 +52,8 @@ public class VerboseHandler extends RepeatingTask {
// if there are any listeners currently registered
private boolean listening = false;
public VerboseHandler() {
super(100, TimeUnit.MILLISECONDS, "luckperms-verbose");
public VerboseHandler(SchedulerAdapter scheduler) {
super(scheduler, 100, TimeUnit.MILLISECONDS);
this.listeners = new ConcurrentHashMap<>();
this.queue = new ConcurrentLinkedQueue<>();
}