Various performance optimizations

This commit is contained in:
Luck 2018-05-15 00:08:23 +01:00
parent a0be1c7c48
commit 18f09f9862
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
72 changed files with 533 additions and 967 deletions

View File

@ -25,95 +25,16 @@
package me.lucko.luckperms.bukkit;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.luckperms.common.plugin.scheduler.AbstractJavaScheduler;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import me.lucko.luckperms.common.utils.Iterators;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
public class BukkitSchedulerAdapter implements SchedulerAdapter {
private final LPBukkitBootstrap bootstrap;
private final ExecutorService asyncFallback;
private final Executor asyncBukkit;
public class BukkitSchedulerAdapter extends AbstractJavaScheduler implements SchedulerAdapter {
private final Executor sync;
private final Executor async;
private boolean useFallback = true;
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
public BukkitSchedulerAdapter(LPBukkitBootstrap bootstrap) {
this.bootstrap = bootstrap;
this.sync = new SyncExecutor();
this.asyncFallback = new FallbackAsyncExecutor();
this.asyncBukkit = new BukkitAsyncExecutor();
this.async = new AsyncExecutor();
}
private BukkitScheduler scheduler() {
return this.bootstrap.getServer().getScheduler();
}
@Override
public void doAsync(Runnable runnable) {
async().execute(runnable);
}
@Override
public void doSync(Runnable runnable) {
sync().execute(runnable);
}
@Override
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
SchedulerTask task = new BukkitSchedulerTask(scheduler().runTaskTimerAsynchronously(this.bootstrap, runnable, intervalTicks, intervalTicks));
this.tasks.add(task);
return task;
}
@Override
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
SchedulerTask task = new BukkitSchedulerTask(scheduler().runTaskTimer(this.bootstrap, runnable, intervalTicks, intervalTicks));
this.tasks.add(task);
return task;
}
@Override
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
return new BukkitSchedulerTask(scheduler().runTaskLaterAsynchronously(this.bootstrap, runnable, delayTicks));
}
@Override
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
return new BukkitSchedulerTask(scheduler().runTaskLater(this.bootstrap, runnable, delayTicks));
}
@Override
public void shutdown() {
Iterators.iterate(this.tasks, SchedulerTask::cancel);
// wait for executor
this.asyncFallback.shutdown();
try {
this.asyncFallback.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.sync = r -> bootstrap.getServer().getScheduler().scheduleSyncDelayedTask(bootstrap, r);
}
@Override
@ -121,62 +42,4 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
return this.sync;
}
@Override
public Executor async() {
return this.async;
}
@Override
public Executor platformAsync() {
return this.asyncBukkit;
}
public void setUseFallback(boolean useFallback) {
this.useFallback = useFallback;
}
private final class SyncExecutor implements Executor {
@Override
public void execute(@Nonnull Runnable runnable) {
BukkitSchedulerAdapter.this.bootstrap.getServer().getScheduler().scheduleSyncDelayedTask(BukkitSchedulerAdapter.this.bootstrap, runnable);
}
}
private final class AsyncExecutor implements Executor {
@Override
public void execute(@Nonnull Runnable runnable) {
if (BukkitSchedulerAdapter.this.useFallback || !BukkitSchedulerAdapter.this.bootstrap.isEnabled()) {
BukkitSchedulerAdapter.this.asyncFallback.execute(runnable);
} else {
BukkitSchedulerAdapter.this.asyncBukkit.execute(runnable);
}
}
}
private final class BukkitAsyncExecutor implements Executor {
@Override
public void execute(@Nonnull Runnable runnable) {
BukkitSchedulerAdapter.this.bootstrap.getServer().getScheduler().runTaskAsynchronously(BukkitSchedulerAdapter.this.bootstrap, runnable);
}
}
private static final class FallbackAsyncExecutor extends ThreadPoolExecutor {
private FallbackAsyncExecutor() {
super(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactoryBuilder().setNameFormat("luckperms-fallback-%d").build());
}
}
private static final class BukkitSchedulerTask implements SchedulerTask {
private final BukkitTask task;
private BukkitSchedulerTask(BukkitTask task) {
this.task = task;
}
@Override
public void cancel() {
this.task.cancel();
}
}
}

View File

@ -79,7 +79,7 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> {
}
// otherwise, send the message sync
getPlugin().getBootstrap().getScheduler().doSync(new SyncMessengerAgent(sender, s));
getPlugin().getBootstrap().getScheduler().executeSync(new SyncMessengerAgent(sender, s));
}
@Override

View File

@ -46,7 +46,7 @@ import me.lucko.luckperms.bukkit.model.server.LPSubscriptionMap;
import me.lucko.luckperms.bukkit.vault.VaultHookManager;
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
import me.lucko.luckperms.common.api.delegates.model.ApiUser;
import me.lucko.luckperms.common.calculators.PlatformCalculatorFactory;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
@ -78,6 +78,7 @@ import java.util.EnumSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
@ -152,7 +153,7 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
}
@Override
protected PlatformCalculatorFactory provideCalculatorFactory() {
protected CalculatorFactory provideCalculatorFactory() {
return new BukkitCalculatorFactory(this);
}
@ -177,7 +178,7 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
// schedule another injection after all plugins have loaded
// the entire pluginmanager instance is replaced by some plugins :(
this.bootstrap.getScheduler().asyncLater(injector, 1L);
this.bootstrap.getServer().getScheduler().runTaskLaterAsynchronously(this.bootstrap, injector, 1);
}
// Provide vault support
@ -214,8 +215,8 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
@Override
protected void registerHousekeepingTasks() {
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 60L);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2400L);
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES);
}
@Override
@ -234,7 +235,7 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
// remove all operators on startup if they're disabled
if (!getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
this.bootstrap.getScheduler().platformAsync().execute(() -> {
this.bootstrap.getServer().getScheduler().runTaskAsynchronously(this.bootstrap, () -> {
for (OfflinePlayer player : this.bootstrap.getServer().getOperators()) {
player.setOp(false);
}
@ -250,16 +251,13 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
});
}
// replace the temporary executor when the Bukkit one starts
this.bootstrap.getServer().getScheduler().runTaskAsynchronously(this.bootstrap, () -> this.bootstrap.getScheduler().setUseFallback(false));
// Load any online users (in the case of a reload)
for (Player player : this.bootstrap.getServer().getOnlinePlayers()) {
this.bootstrap.getScheduler().doAsync(() -> {
this.bootstrap.getScheduler().executeAsync(() -> {
try {
User user = this.connectionListener.loadUser(player.getUniqueId(), player.getName());
if (user != null) {
this.bootstrap.getScheduler().doSync(() -> {
this.bootstrap.getScheduler().executeSync(() -> {
try {
LPPermissible lpPermissible = new LPPermissible(player, user, this);
PermissibleInjector.inject(player, lpPermissible);
@ -275,12 +273,6 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin {
}
}
@Override
protected void performEarlyDisableTasks() {
// Switch back to the fallback executor, the bukkit one won't allow new tasks
this.bootstrap.getScheduler().setUseFallback(true);
}
@Override
protected void removePlatformHooks() {
// uninject from players

View File

@ -32,7 +32,7 @@ import me.lucko.luckperms.api.LookupSetting;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.bukkit.processors.ChildProcessor;
import me.lucko.luckperms.bukkit.processors.DefaultsProcessor;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.config.ConfigKeys;
@ -42,7 +42,7 @@ import me.lucko.luckperms.common.processors.PermissionProcessor;
import me.lucko.luckperms.common.processors.RegexProcessor;
import me.lucko.luckperms.common.processors.WildcardProcessor;
public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
public class BukkitCalculatorFactory implements CalculatorFactory {
private final LPBukkitPlugin plugin;
public BukkitCalculatorFactory(LPBukkitPlugin plugin) {
@ -71,6 +71,6 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new DefaultsProcessor(this.plugin, contexts.hasSetting(LookupSetting.IS_OP)));
}
return registerCalculator(new PermissionCalculator(this.plugin, metadata, processors.build()));
return new PermissionCalculator(this.plugin, metadata, processors.build());
}
}

View File

@ -191,7 +191,7 @@ public class BukkitConnectionListener extends AbstractConnectionListener impleme
this.plugin.getUserManager().getHouseKeeper().registerUsage(player.getUniqueId());
// force a clear of transient nodes
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
if (user != null) {
user.clearTransientNodes();

View File

@ -87,7 +87,7 @@ public class LilyPadMessenger implements Messenger {
@EventListener
public void onMessage(MessageEvent event) {
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
try {
String channel = event.getChannel();

View File

@ -189,7 +189,6 @@ public class LPPermissionAttachment extends PermissionAttachment {
// remove transient permissions from the holder which were added by this attachment & equal the permission
User user = this.permissible.getUser();
user.removeIfTransient(LocalizedNode.composedPredicate(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name)));
}

View File

@ -33,6 +33,7 @@ import com.google.common.collect.Maps;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.buffers.Cache;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
@ -67,8 +68,8 @@ public final class LPDefaultsMap implements Map<Boolean, Set<Permission>> {
private final Set<Permission> nonOpSet = new DefaultPermissionSet(false);
// fully resolved defaults (accounts for child permissions too)
private Map<String, Boolean> resolvedOpDefaults = ImmutableMap.of();
private Map<String, Boolean> resolvedNonOpDefaults = ImmutableMap.of();
private DefaultsCache opCache = new DefaultsCache(true);
private DefaultsCache nonOpCache = new DefaultsCache(false);
// #values and #entrySet results - both immutable
private final Collection<Set<Permission>> values = ImmutableList.of(this.opSet, this.nonOpSet);
@ -81,16 +82,20 @@ public final class LPDefaultsMap implements Map<Boolean, Set<Permission>> {
this.plugin = plugin;
this.opSet.addAll(existingData.getOrDefault(Boolean.TRUE, Collections.emptySet()));
this.nonOpSet.addAll(existingData.getOrDefault(Boolean.FALSE, Collections.emptySet()));
refreshOp();
refreshNonOp();
}
public Set<Permission> getOpPermissions() {
return this.opSet;
@Override
public Set<Permission> get(Object key) {
boolean b = (boolean) key;
return b ? this.opSet : this.nonOpSet;
}
public Set<Permission> getNonOpPermissions() {
return this.nonOpSet;
private DefaultsCache getCache(boolean op) {
return op ? this.opCache : this.nonOpCache;
}
private void invalidate(boolean op) {
getCache(op).invalidate();
}
/**
@ -101,54 +106,10 @@ public final class LPDefaultsMap implements Map<Boolean, Set<Permission>> {
* @return a tristate result
*/
public Tristate lookupDefaultPermission(String permission, boolean isOp) {
Map<String, Boolean> map = isOp ? this.resolvedOpDefaults : this.resolvedNonOpDefaults;
Map<String, Boolean> map = getCache(isOp).get();
return Tristate.fromNullableBoolean(map.get(permission));
}
private void refresh(boolean op) {
if (op) {
refreshOp();
} else {
refreshNonOp();
}
}
/**
* Refreshes the op data in this provider.
*/
private void refreshOp() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : getOpPermissions()) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
this.resolvedOpDefaults = ImmutableMap.copyOf(builder);
}
/**
* Refreshes the non op data in this provider.
*/
private void refreshNonOp() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : getNonOpPermissions()) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
this.resolvedNonOpDefaults = ImmutableMap.copyOf(builder);
}
@Override
public Set<Permission> get(Object key) {
boolean b = (boolean) key;
return b ? this.opSet : this.nonOpSet;
}
// return wrappers around this map impl
@Nonnull @Override public Collection<Set<Permission>> values() { return this.values; }
@Nonnull @Override public Set<Entry<Boolean, Set<Permission>>> entrySet() { return this.entrySet; }
@ -166,11 +127,33 @@ public final class LPDefaultsMap implements Map<Boolean, Set<Permission>> {
@Override public void putAll(@Nonnull Map<? extends Boolean, ? extends Set<Permission>> m) { throw new UnsupportedOperationException(); }
@Override public void clear() { throw new UnsupportedOperationException(); }
private final class DefaultsCache extends Cache<Map<String, Boolean>> {
private final boolean op;
DefaultsCache(boolean op) {
this.op = op;
}
@Nonnull
@Override
protected Map<String, Boolean> supply() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : LPDefaultsMap.this.get(this.op)) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : LPDefaultsMap.this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
return ImmutableMap.copyOf(builder);
}
}
private final class DefaultPermissionSet extends ForwardingSet<Permission> {
private final Set<Permission> delegate = ConcurrentHashMap.newKeySet();
private final boolean op;
private DefaultPermissionSet(boolean op) {
DefaultPermissionSet(boolean op) {
this.op = op;
}
@ -182,14 +165,14 @@ public final class LPDefaultsMap implements Map<Boolean, Set<Permission>> {
@Override
public boolean add(@Nonnull Permission element) {
boolean ret = super.add(element);
refresh(this.op);
invalidate(this.op);
return ret;
}
@Override
public boolean addAll(@Nonnull Collection<? extends Permission> collection) {
boolean ret = super.addAll(collection);
refresh(this.op);
invalidate(this.op);
return ret;
}
}

View File

@ -25,104 +25,56 @@
package me.lucko.luckperms.bungee;
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.utils.Iterators;
import net.md_5.bungee.api.scheduler.ScheduledTask;
import net.md_5.bungee.api.scheduler.TaskScheduler;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
public class BungeeSchedulerAdapter implements SchedulerAdapter {
// the number of ticks which occur in a second - this is a server implementation detail
public static final int TICKS_PER_SECOND = 20;
// the number of milliseconds in a second - constant
public static final int MILLISECONDS_PER_SECOND = 1000;
// the number of milliseconds in a tick - assuming the server runs at a perfect tick rate
public static final int MILLISECONDS_PER_TICK = MILLISECONDS_PER_SECOND / TICKS_PER_SECOND;
private static long ticksToMillis(long ticks) {
return ticks * MILLISECONDS_PER_TICK;
}
private final LPBungeeBootstrap bootstrap;
private final Executor asyncExecutor;
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
private final Executor executor;
private final Set<ScheduledTask> tasks = Collections.newSetFromMap(new WeakHashMap<>());
public BungeeSchedulerAdapter(LPBungeeBootstrap bootstrap) {
this.bootstrap = bootstrap;
this.asyncExecutor = r -> bootstrap.getProxy().getScheduler().runAsync(bootstrap, r);
}
private TaskScheduler scheduler() {
return this.bootstrap.getProxy().getScheduler();
this.executor = r -> bootstrap.getProxy().getScheduler().runAsync(bootstrap, r);
}
@Override
public Executor async() {
return this.asyncExecutor;
return this.executor;
}
@Override
public Executor sync() {
return this.asyncExecutor;
return this.executor;
}
@Override
public void doAsync(Runnable runnable) {
this.asyncExecutor.execute(runnable);
public SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit) {
ScheduledTask t = this.bootstrap.getProxy().getScheduler().schedule(this.bootstrap, task, delay, unit);
this.tasks.add(t);
return t::cancel;
}
@Override
public void doSync(Runnable runnable) {
doAsync(runnable);
}
@Override
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
long millis = ticksToMillis(intervalTicks);
SchedulerTask task = new BungeeSchedulerTask(scheduler().schedule(this.bootstrap, runnable, millis, millis, TimeUnit.MILLISECONDS));
this.tasks.add(task);
return task;
}
@Override
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
return asyncRepeating(runnable, intervalTicks);
}
@Override
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
return new BungeeSchedulerTask(scheduler().schedule(this.bootstrap, runnable, ticksToMillis(delayTicks), TimeUnit.MILLISECONDS));
}
@Override
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
return asyncLater(runnable, delayTicks);
public SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit) {
ScheduledTask t = this.bootstrap.getProxy().getScheduler().schedule(this.bootstrap, task, interval, interval, unit);
this.tasks.add(t);
return t::cancel;
}
@Override
public void shutdown() {
Iterators.iterate(this.tasks, SchedulerTask::cancel);
}
private static final class BungeeSchedulerTask implements SchedulerTask {
private final ScheduledTask task;
private BungeeSchedulerTask(ScheduledTask task) {
this.task = task;
}
@Override
public void cancel() {
this.task.cancel();
}
Iterators.iterate(this.tasks, ScheduledTask::cancel);
}
}

View File

@ -29,8 +29,8 @@ import me.lucko.luckperms.api.platform.PlatformType;
import me.lucko.luckperms.bungee.util.RedisBungeeUtil;
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
import me.lucko.luckperms.common.dependencies.classloader.ReflectionClassLoader;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;

View File

@ -35,7 +35,7 @@ import me.lucko.luckperms.bungee.listeners.BungeeConnectionListener;
import me.lucko.luckperms.bungee.listeners.BungeePermissionCheckListener;
import me.lucko.luckperms.bungee.messaging.BungeeMessagingFactory;
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
import me.lucko.luckperms.common.calculators.PlatformCalculatorFactory;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.command.CommandManager;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
import me.lucko.luckperms.common.contexts.ContextManager;
@ -61,6 +61,7 @@ import java.nio.file.Files;
import java.util.EnumSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
@ -130,7 +131,7 @@ public class LPBungeePlugin extends AbstractLuckPermsPlugin {
}
@Override
protected PlatformCalculatorFactory provideCalculatorFactory() {
protected CalculatorFactory provideCalculatorFactory() {
return new BungeeCalculatorFactory(this);
}
@ -161,8 +162,8 @@ public class LPBungeePlugin extends AbstractLuckPermsPlugin {
@Override
protected void registerHousekeepingTasks() {
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 60L);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2400L);
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES);
}
@Override

View File

@ -29,7 +29,7 @@ import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.bungee.LPBungeePlugin;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.config.ConfigKeys;
@ -38,7 +38,7 @@ import me.lucko.luckperms.common.processors.PermissionProcessor;
import me.lucko.luckperms.common.processors.RegexProcessor;
import me.lucko.luckperms.common.processors.WildcardProcessor;
public class BungeeCalculatorFactory extends AbstractCalculatorFactory {
public class BungeeCalculatorFactory implements CalculatorFactory {
private final LPBungeePlugin plugin;
public BungeeCalculatorFactory(LPBungeePlugin plugin) {
@ -59,6 +59,6 @@ public class BungeeCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new WildcardProcessor());
}
return registerCalculator(new PermissionCalculator(this.plugin, metadata, processors.build()));
return new PermissionCalculator(this.plugin, metadata, processors.build());
}
}

View File

@ -70,7 +70,7 @@ public class BungeeConnectionListener extends AbstractConnectionListener impleme
this.plugin.getLogger().info("Processing pre-login for " + c.getUniqueId() + " - " + c.getName());
}
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
recordConnection(c.getUniqueId());
/* Actually process the login for the connection.
@ -139,7 +139,7 @@ public class BungeeConnectionListener extends AbstractConnectionListener impleme
this.plugin.getUserManager().getHouseKeeper().registerUsage(player.getUniqueId());
// force a clear of transient nodes
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
if (user != null) {
user.clearTransientNodes();

View File

@ -97,7 +97,7 @@ public class BungeeMessenger implements Messenger, Listener {
if (this.consumer.consumeIncomingMessageAsString(msg)) {
// Forward to other servers
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
for (ServerInfo server : this.plugin.getBootstrap().getProxy().getServers().values()) {
server.sendData(CHANNEL, data, true);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -52,7 +52,7 @@ public final class ImmutableLocalizedNode extends ForwardingNode implements Loca
}
@Override
protected Node delegate() {
public Node delegate() {
return this.node;
}

View File

@ -85,7 +85,6 @@ public final class ImmutableNode implements Node {
// cached state
private final Optional<String> optServer;
private final Optional<String> optWorld;

View File

@ -48,7 +48,7 @@ public final class ImmutableTransientNode<O> extends ForwardingNode implements N
}
@Override
protected Node delegate() {
public Node delegate() {
return this.node;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.event.user.UserDataRecalculateEvent;
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
import me.lucko.luckperms.common.api.delegates.model.ApiUser;
import me.lucko.luckperms.common.calculators.PlatformCalculatorFactory;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
@ -75,6 +75,7 @@ import java.util.EnumSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
@ -145,7 +146,7 @@ public class LPNukkitPlugin extends AbstractLuckPermsPlugin {
}
@Override
protected PlatformCalculatorFactory provideCalculatorFactory() {
protected CalculatorFactory provideCalculatorFactory() {
return new NukkitCalculatorFactory(this);
}
@ -170,7 +171,7 @@ public class LPNukkitPlugin extends AbstractLuckPermsPlugin {
// schedule another injection after all plugins have loaded
// the entire pluginmanager instance is replaced by some plugins :(
this.bootstrap.getScheduler().asyncLater(injector, 1L);
this.bootstrap.getServer().getScheduler().scheduleDelayedTask(this.bootstrap, injector, 1, true);
}
}
@ -186,8 +187,8 @@ public class LPNukkitPlugin extends AbstractLuckPermsPlugin {
@Override
protected void registerHousekeepingTasks() {
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 60L);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2400L);
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES);
}
@Override
@ -219,16 +220,13 @@ public class LPNukkitPlugin extends AbstractLuckPermsPlugin {
});
}
// replace the temporary executor when the Nukkit one starts
this.bootstrap.getServer().getScheduler().scheduleTask(this.bootstrap, () -> this.bootstrap.getScheduler().setUseFallback(false), true);
// Load any online users (in the case of a reload)
for (Player player : this.bootstrap.getServer().getOnlinePlayers().values()) {
this.bootstrap.getScheduler().doAsync(() -> {
this.bootstrap.getScheduler().executeAsync(() -> {
try {
User user = this.connectionListener.loadUser(player.getUniqueId(), player.getName());
if (user != null) {
this.bootstrap.getScheduler().doSync(() -> {
this.bootstrap.getScheduler().executeSync(() -> {
try {
LPPermissible lpPermissible = new LPPermissible(player, user, this);
PermissibleInjector.inject(player, lpPermissible);
@ -244,12 +242,6 @@ public class LPNukkitPlugin extends AbstractLuckPermsPlugin {
}
}
@Override
protected void performEarlyDisableTasks() {
// Switch back to the fallback executor, the nukkit one won't allow new tasks
this.bootstrap.getScheduler().setUseFallback(true);
}
@Override
protected void removePlatformHooks() {
// uninject from players

View File

@ -25,95 +25,16 @@
package me.lucko.luckperms.nukkit;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.luckperms.common.plugin.scheduler.AbstractJavaScheduler;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import me.lucko.luckperms.common.utils.Iterators;
import cn.nukkit.scheduler.ServerScheduler;
import cn.nukkit.scheduler.TaskHandler;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
public class NukkitSchedulerAdapter implements SchedulerAdapter {
private final LPNukkitBootstrap bootstrap;
private final ExecutorService asyncFallback;
private final Executor asyncNukkit;
public class NukkitSchedulerAdapter extends AbstractJavaScheduler implements SchedulerAdapter {
private final Executor sync;
private final Executor async;
private boolean useFallback = true;
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
public NukkitSchedulerAdapter(LPNukkitBootstrap bootstrap) {
this.bootstrap = bootstrap;
this.sync = new SyncExecutor();
this.asyncFallback = new FallbackAsyncExecutor();
this.asyncNukkit = new NukkitAsyncExecutor();
this.async = new AsyncExecutor();
}
private ServerScheduler scheduler() {
return this.bootstrap.getServer().getScheduler();
}
@Override
public void doAsync(Runnable runnable) {
async().execute(runnable);
}
@Override
public void doSync(Runnable runnable) {
sync().execute(runnable);
}
@Override
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
SchedulerTask task = new NukkitSchedulerTask(scheduler().scheduleDelayedRepeatingTask(this.bootstrap, runnable, (int) intervalTicks, (int) intervalTicks, true));
this.tasks.add(task);
return task;
}
@Override
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
SchedulerTask task = new NukkitSchedulerTask(scheduler().scheduleDelayedRepeatingTask(this.bootstrap, runnable, (int) intervalTicks, (int) intervalTicks, false));
this.tasks.add(task);
return task;
}
@Override
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
return new NukkitSchedulerTask(scheduler().scheduleDelayedTask(this.bootstrap, runnable, (int) delayTicks, true));
}
@Override
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
return new NukkitSchedulerTask(scheduler().scheduleDelayedTask(this.bootstrap, runnable, (int) delayTicks, false));
}
@Override
public void shutdown() {
Iterators.iterate(this.tasks, SchedulerTask::cancel);
// wait for executor
this.asyncFallback.shutdown();
try {
this.asyncFallback.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.sync = r -> bootstrap.getServer().getScheduler().scheduleTask(bootstrap, r, false);
}
@Override
@ -121,62 +42,4 @@ public class NukkitSchedulerAdapter implements SchedulerAdapter {
return this.sync;
}
@Override
public Executor async() {
return this.async;
}
@Override
public Executor platformAsync() {
return this.asyncNukkit;
}
public void setUseFallback(boolean useFallback) {
this.useFallback = useFallback;
}
private final class SyncExecutor implements Executor {
@Override
public void execute(@Nonnull Runnable runnable) {
NukkitSchedulerAdapter.this.bootstrap.getServer().getScheduler().scheduleTask(NukkitSchedulerAdapter.this.bootstrap, runnable, false);
}
}
private final class AsyncExecutor implements Executor {
@Override
public void execute(@Nonnull Runnable runnable) {
if (NukkitSchedulerAdapter.this.useFallback || !NukkitSchedulerAdapter.this.bootstrap.isEnabled()) {
NukkitSchedulerAdapter.this.asyncFallback.execute(runnable);
} else {
NukkitSchedulerAdapter.this.asyncNukkit.execute(runnable);
}
}
}
private final class NukkitAsyncExecutor implements Executor {
@Override
public void execute(@Nonnull Runnable runnable) {
NukkitSchedulerAdapter.this.bootstrap.getServer().getScheduler().scheduleTask(NukkitSchedulerAdapter.this.bootstrap, runnable, true);
}
}
private static final class FallbackAsyncExecutor extends ThreadPoolExecutor {
private FallbackAsyncExecutor() {
super(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactoryBuilder().setNameFormat("luckperms-fallback-%d").build());
}
}
private static final class NukkitSchedulerTask implements SchedulerTask {
private final TaskHandler task;
private NukkitSchedulerTask(TaskHandler task) {
this.task = task;
}
@Override
public void cancel() {
this.task.cancel();
}
}
}

View File

@ -69,7 +69,7 @@ public class NukkitSenderFactory extends SenderFactory<CommandSender> {
}
// otherwise, send the message sync
getPlugin().getBootstrap().getScheduler().doSync(new SyncMessengerAgent(sender, s));
getPlugin().getBootstrap().getScheduler().executeSync(new SyncMessengerAgent(sender, s));
}
@Override

View File

@ -29,7 +29,7 @@ import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LookupSetting;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.config.ConfigKeys;
@ -42,7 +42,7 @@ import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import me.lucko.luckperms.nukkit.processors.ChildProcessor;
import me.lucko.luckperms.nukkit.processors.DefaultsProcessor;
public class NukkitCalculatorFactory extends AbstractCalculatorFactory {
public class NukkitCalculatorFactory implements CalculatorFactory {
private final LPNukkitPlugin plugin;
public NukkitCalculatorFactory(LPNukkitPlugin plugin) {
@ -71,6 +71,6 @@ public class NukkitCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new DefaultsProcessor(this.plugin, contexts.hasSetting(LookupSetting.IS_OP)));
}
return registerCalculator(new PermissionCalculator(this.plugin, metadata, processors.build()));
return new PermissionCalculator(this.plugin, metadata, processors.build());
}
}

View File

@ -183,7 +183,7 @@ public class NukkitConnectionListener extends AbstractConnectionListener impleme
this.plugin.getUserManager().getHouseKeeper().registerUsage(player.getUniqueId());
// force a clear of transient nodes
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
if (user != null) {
user.clearTransientNodes();

View File

@ -29,6 +29,7 @@ import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.buffers.Cache;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import cn.nukkit.permission.Permission;
@ -39,6 +40,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull;
/**
* A replacement map for the 'defaultPerms' instance in Nukkit's SimplePluginManager.
*
@ -58,15 +61,13 @@ public final class LPDefaultsMap {
private final Map<String, Permission> nonOpSet = new DefaultPermissionSet(false);
// fully resolved defaults (accounts for child permissions too)
private Map<String, Boolean> resolvedOpDefaults = ImmutableMap.of();
private Map<String, Boolean> resolvedNonOpDefaults = ImmutableMap.of();
private DefaultsCache opCache = new DefaultsCache(true);
private DefaultsCache nonOpCache = new DefaultsCache(false);
public LPDefaultsMap(LPNukkitPlugin plugin, Map<Boolean, Map<String, Permission>> existingData) {
this.plugin = plugin;
this.opSet.putAll(existingData.getOrDefault(Boolean.TRUE, Collections.emptyMap()));
this.nonOpSet.putAll(existingData.getOrDefault(Boolean.FALSE, Collections.emptyMap()));
refreshOp();
refreshNonOp();
}
public Map<String, Permission> getOpPermissions() {
@ -77,6 +78,18 @@ public final class LPDefaultsMap {
return this.nonOpSet;
}
public Map<String, Permission> get(boolean op) {
return op ? this.opSet : this.nonOpSet;
}
private DefaultsCache getCache(boolean op) {
return op ? this.opCache : this.nonOpCache;
}
private void invalidate(boolean op) {
getCache(op).invalidate();
}
/**
* Queries whether a given permission should be granted by default.
*
@ -85,55 +98,17 @@ public final class LPDefaultsMap {
* @return a tristate result
*/
public Tristate lookupDefaultPermission(String permission, boolean isOp) {
Map<String, Boolean> map = isOp ? this.resolvedOpDefaults : this.resolvedNonOpDefaults;
Map<String, Boolean> map = getCache(isOp).get();
return Tristate.fromNullableBoolean(map.get(permission));
}
private void refresh(boolean op) {
if (op) {
refreshOp();
} else {
refreshNonOp();
}
}
/**
* Refreshes the op data in this provider.
*/
private void refreshOp() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : getOpPermissions().values()) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
this.resolvedOpDefaults = ImmutableMap.copyOf(builder);
}
/**
* Refreshes the non op data in this provider.
*/
private void refreshNonOp() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : getNonOpPermissions().values()) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
this.resolvedNonOpDefaults = ImmutableMap.copyOf(builder);
}
final class DefaultPermissionSet extends ForwardingMap<String, Permission> {
final LPDefaultsMap parent = LPDefaultsMap.this;
private final Map<String, Permission> delegate = new ConcurrentHashMap<>();
private final boolean op;
private DefaultPermissionSet(boolean op) {
DefaultPermissionSet(boolean op) {
this.op = op;
}
@ -145,21 +120,43 @@ public final class LPDefaultsMap {
@Override
public Permission put(String key, Permission value) {
Permission ret = super.put(key, value);
refresh(this.op);
invalidate(this.op);
return ret;
}
@Override
public Permission putIfAbsent(String key, Permission value) {
Permission ret = super.putIfAbsent(key, value);
refresh(this.op);
invalidate(this.op);
return ret;
}
@Override
public void putAll(Map<? extends String, ? extends Permission> map) {
super.putAll(map);
refresh(this.op);
invalidate(this.op);
}
}
private final class DefaultsCache extends Cache<Map<String, Boolean>> {
private final boolean op;
DefaultsCache(boolean op) {
this.op = op;
}
@Nonnull
@Override
protected Map<String, Boolean> supply() {
Map<String, Boolean> builder = new HashMap<>();
for (Permission perm : LPDefaultsMap.this.get(this.op).values()) {
String name = perm.getName().toLowerCase();
builder.put(name, true);
for (Map.Entry<String, Boolean> child : LPDefaultsMap.this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) {
builder.putIfAbsent(child.getKey(), child.getValue());
}
}
return ImmutableMap.copyOf(builder);
}
}

View File

@ -25,8 +25,6 @@
package me.lucko.luckperms.sponge.service;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.Tristate;
@ -39,19 +37,12 @@ import org.spongepowered.api.service.context.Context;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Utility class for converting between Sponge and LuckPerms context and tristate classes
*/
public final class CompatibilityUtil {
private static final LoadingCache<Set<Context>, ImmutableContextSet> SPONGE_TO_LP_CACHE = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(ImmutableContextSet::fromEntries);
private static final LoadingCache<ImmutableContextSet, Set<Context>> LP_TO_SPONGE_CACHE = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(DelegatingImmutableContextSet::new);
private static final Set<Context> EMPTY = ImmutableSet.of();
public static ImmutableContextSet convertContexts(Set<Context> contexts) {
Objects.requireNonNull(contexts, "contexts");
@ -60,12 +51,21 @@ public final class CompatibilityUtil {
return ((DelegatingContextSet) contexts).getDelegate().makeImmutable();
}
return SPONGE_TO_LP_CACHE.get(ImmutableSet.copyOf(contexts));
if (contexts.isEmpty()) {
return ImmutableContextSet.empty();
}
return ImmutableContextSet.fromEntries(contexts);
}
public static Set<Context> convertContexts(ContextSet contexts) {
Objects.requireNonNull(contexts, "contexts");
return LP_TO_SPONGE_CACHE.get(contexts.makeImmutable());
if (contexts.isEmpty()) {
return EMPTY;
}
return new DelegatingImmutableContextSet(contexts.makeImmutable());
}
public static org.spongepowered.api.util.Tristate convertTristate(Tristate tristate) {

View File

@ -60,12 +60,13 @@ public final class SubjectReferenceFactory {
*
* It's perfectly ok if two instances of the same SubjectReference exist. (hence the 1 hour expiry)
*/
private final LoadingCache<SubjectReferenceAttributes, CachedSubjectReference> referenceCache = Caffeine.newBuilder()
.expireAfterAccess(1, TimeUnit.HOURS)
.build(a -> new CachedSubjectReference(SubjectReferenceFactory.this.service, a.collectionId, a.id));
private final LoadingCache<SubjectReferenceAttributes, CachedSubjectReference> referenceCache;
public SubjectReferenceFactory(LPPermissionService service) {
this.service = service;
this.referenceCache = Caffeine.newBuilder()
.expireAfterAccess(1, TimeUnit.HOURS)
.build(a -> new CachedSubjectReference(this.service, a.collectionId, a.id));
}
@Deprecated

View File

@ -30,8 +30,8 @@ import com.google.inject.Inject;
import me.lucko.luckperms.api.platform.PlatformType;
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
import me.lucko.luckperms.common.dependencies.classloader.ReflectionClassLoader;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import me.lucko.luckperms.common.utils.MoreFiles;
import me.lucko.luckperms.sponge.utils.VersionData;

View File

@ -28,7 +28,7 @@ package me.lucko.luckperms.sponge;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
import me.lucko.luckperms.common.calculators.PlatformCalculatorFactory;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.command.CommandManager;
import me.lucko.luckperms.common.command.abstraction.Command;
import me.lucko.luckperms.common.command.access.CommandPermission;
@ -75,6 +75,7 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
@ -146,7 +147,7 @@ public class LPSpongePlugin extends AbstractLuckPermsPlugin {
}
@Override
protected PlatformCalculatorFactory provideCalculatorFactory() {
protected CalculatorFactory provideCalculatorFactory() {
return new SpongeCalculatorFactory(this);
}
@ -189,9 +190,9 @@ public class LPSpongePlugin extends AbstractLuckPermsPlugin {
@Override
protected void registerHousekeepingTasks() {
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 60L);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2400L);
this.bootstrap.getScheduler().asyncRepeating(new ServiceCacheHousekeepingTask(this.service), 2400L);
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES);
this.bootstrap.getScheduler().asyncRepeating(new ServiceCacheHousekeepingTask(this.service), 2, TimeUnit.MINUTES);
}
@Override

View File

@ -25,17 +25,19 @@
package me.lucko.luckperms.sponge;
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.utils.Iterators;
import org.spongepowered.api.scheduler.Scheduler;
import org.spongepowered.api.scheduler.SpongeExecutorService;
import org.spongepowered.api.scheduler.Task;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
public class SpongeSchedulerAdapter implements SchedulerAdapter {
private final LPSpongeBootstrap bootstrap;
@ -44,7 +46,7 @@ public class SpongeSchedulerAdapter implements SchedulerAdapter {
private final SpongeExecutorService sync;
private final SpongeExecutorService async;
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
private final Set<Task> tasks = Collections.newSetFromMap(new WeakHashMap<>());
public SpongeSchedulerAdapter(LPSpongeBootstrap bootstrap, Scheduler scheduler, SpongeExecutorService sync, SpongeExecutorService async) {
this.bootstrap = bootstrap;
@ -64,82 +66,43 @@ public class SpongeSchedulerAdapter implements SchedulerAdapter {
}
@Override
public void doAsync(Runnable runnable) {
public void executeAsync(Runnable runnable) {
this.scheduler.createTaskBuilder().async().execute(runnable).submit(this.bootstrap);
}
@Override
public void doSync(Runnable runnable) {
public void executeSync(Runnable runnable) {
this.scheduler.createTaskBuilder().execute(runnable).submit(this.bootstrap);
}
@Override
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
Task task = this.scheduler.createTaskBuilder()
public SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit) {
Task t = this.scheduler.createTaskBuilder()
.async()
.intervalTicks(intervalTicks)
.delayTicks(intervalTicks)
.execute(runnable)
.delay(delay, unit)
.execute(task)
.submit(this.bootstrap);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
this.tasks.add(t);
return t::cancel;
}
@Override
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
Task task = this.scheduler.createTaskBuilder()
.intervalTicks(intervalTicks)
.delayTicks(intervalTicks)
.execute(runnable)
.submit(this.bootstrap);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
}
@Override
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
Task task = this.scheduler.createTaskBuilder()
public SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit) {
Task t = this.scheduler.createTaskBuilder()
.async()
.delayTicks(delayTicks)
.execute(runnable)
.interval(interval, unit)
.delay(interval, unit)
.execute(task)
.submit(this.bootstrap);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
}
@Override
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
Task task = this.scheduler.createTaskBuilder()
.delayTicks(delayTicks)
.execute(runnable)
.submit(this.bootstrap);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
this.tasks.add(t);
return t::cancel;
}
@Override
public void shutdown() {
Iterators.iterate(this.tasks, SchedulerTask::cancel);
Iterators.iterate(this.tasks, Task::cancel);
}
private static final class SpongeSchedulerTask implements SchedulerTask {
private final Task task;
private SpongeSchedulerTask(Task task) {
this.task = task;
}
@Override
public void cancel() {
this.task.cancel();
}
}
}

View File

@ -28,7 +28,7 @@ package me.lucko.luckperms.sponge.calculators;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.config.ConfigKeys;
@ -41,7 +41,7 @@ import me.lucko.luckperms.sponge.processors.GroupDefaultsProcessor;
import me.lucko.luckperms.sponge.processors.SpongeWildcardProcessor;
import me.lucko.luckperms.sponge.processors.UserDefaultsProcessor;
public class SpongeCalculatorFactory extends AbstractCalculatorFactory {
public class SpongeCalculatorFactory implements CalculatorFactory {
private final LPSpongePlugin plugin;
public SpongeCalculatorFactory(LPSpongePlugin plugin) {
@ -74,6 +74,6 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory {
}
}
return registerCalculator(new PermissionCalculator(this.plugin, metadata, processors.build()));
return new PermissionCalculator(this.plugin, metadata, processors.build());
}
}

View File

@ -165,7 +165,7 @@ public class SpongeConnectionListener extends AbstractConnectionListener {
this.plugin.getUserManager().getHouseKeeper().registerUsage(player.getUniqueId());
// force a clear of transient nodes
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
if (user != null) {
user.clearTransientNodes();

View File

@ -237,8 +237,6 @@ public class LuckPermsService implements LPPermissionService {
subject.invalidateCaches();
}
}
this.plugin.getCalculatorFactory().invalidateAll();
}
}

View File

@ -39,7 +39,7 @@ public class UpdateEventHandlerImpl implements UpdateEventHandler {
@Override
public void fireUpdateEvent(LPSubjectData subjectData) {
this.plugin.getBootstrap().getScheduler().doAsync(() -> {
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
SubjectDataUpdateEvent event = new LPSubjectDataUpdateEvent(this.plugin, subjectData);
this.plugin.getBootstrap().getGame().getEventManager().post(event);
});

View File

@ -161,7 +161,7 @@ public abstract class HolderSubject<T extends PermissionHolder> implements LPSub
@Override
public void invalidateCaches() {
// invalidate for all changes
this.parent.getCachedData().invalidate();
this.parent.invalidateCachedData();
}
}

View File

@ -35,7 +35,6 @@ 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.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.LPSubjectData;
@ -204,7 +203,7 @@ public class SubjectDataContainer {
// sort alphabetically.
List<Map.Entry<String, Boolean>> perms = new ArrayList<>(e.getValue().entrySet());
perms.sort(Map.Entry.comparingByKey(CollationKeyCache.comparator()));
perms.sort(Map.Entry.comparingByKey());
for (Map.Entry<String, Boolean> ent : perms) {
data.addProperty(ent.getKey(), ent.getValue());
@ -228,7 +227,7 @@ public class SubjectDataContainer {
// sort alphabetically.
List<Map.Entry<String, String>> opts = new ArrayList<>(e.getValue().entrySet());
opts.sort(Map.Entry.comparingByKey(CollationKeyCache.comparator()));
opts.sort(Map.Entry.comparingByKey());
for (Map.Entry<String, String> ent : opts) {
data.addProperty(ent.getKey(), ent.getValue());