Rewrite the way user instances are cleaned up and unloaded - towards #674

This commit is contained in:
Luck 2018-01-10 21:28:33 +00:00
parent ea94bd8696
commit 3201d10bdd
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
36 changed files with 725 additions and 468 deletions

View File

@ -28,7 +28,10 @@ package me.lucko.luckperms.bukkit;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import me.lucko.luckperms.common.utils.SafeIterator;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.util.Set;
@ -51,7 +54,7 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
private boolean useFallback = true;
private final Set<BukkitTask> tasks = ConcurrentHashMap.newKeySet();
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
public BukkitSchedulerAdapter(LPBukkitPlugin plugin) {
this.plugin = plugin;
@ -62,6 +65,10 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
this.async = new AsyncExecutor();
}
private BukkitScheduler scheduler() {
return this.plugin.getServer().getScheduler();
}
@Override
public void doAsync(Runnable runnable) {
async().execute(runnable);
@ -73,30 +80,32 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
}
@Override
public void asyncRepeating(Runnable runnable, long intervalTicks) {
BukkitTask task = this.plugin.getServer().getScheduler().runTaskTimerAsynchronously(this.plugin, runnable, intervalTicks, intervalTicks);
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
SchedulerTask task = new BukkitSchedulerTask(scheduler().runTaskTimerAsynchronously(this.plugin, runnable, intervalTicks, intervalTicks));
this.tasks.add(task);
return task;
}
@Override
public void syncRepeating(Runnable runnable, long intervalTicks) {
BukkitTask task = this.plugin.getServer().getScheduler().runTaskTimer(this.plugin, runnable, intervalTicks, intervalTicks);
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
SchedulerTask task = new BukkitSchedulerTask(scheduler().runTaskTimer(this.plugin, runnable, intervalTicks, intervalTicks));
this.tasks.add(task);
return task;
}
@Override
public void asyncLater(Runnable runnable, long delayTicks) {
this.plugin.getServer().getScheduler().runTaskLaterAsynchronously(this.plugin, runnable, delayTicks);
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
return new BukkitSchedulerTask(scheduler().runTaskLaterAsynchronously(this.plugin, runnable, delayTicks));
}
@Override
public void syncLater(Runnable runnable, long delayTicks) {
this.plugin.getServer().getScheduler().scheduleSyncDelayedTask(this.plugin, runnable, delayTicks);
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
return new BukkitSchedulerTask(scheduler().runTaskLater(this.plugin, runnable, delayTicks));
}
@Override
public void shutdown() {
this.tasks.forEach(BukkitTask::cancel);
SafeIterator.iterate(this.tasks, SchedulerTask::cancel);
// wait for executor
this.asyncFallback.shutdown();
@ -161,4 +170,17 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
}
}
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

@ -63,12 +63,9 @@ import me.lucko.luckperms.common.locale.NoopLocaleManager;
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.logging.Logger;
import me.lucko.luckperms.common.logging.SenderLogger;
import me.lucko.luckperms.common.managers.GenericGroupManager;
import me.lucko.luckperms.common.managers.GenericTrackManager;
import me.lucko.luckperms.common.managers.GenericUserManager;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.managers.group.StandardGroupManager;
import me.lucko.luckperms.common.managers.track.StandardTrackManager;
import me.lucko.luckperms.common.managers.user.StandardUserManager;
import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@ -80,7 +77,6 @@ import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
import me.lucko.luckperms.common.tasks.UpdateTask;
import me.lucko.luckperms.common.treeview.PermissionVault;
import me.lucko.luckperms.common.utils.LoginHelper;
import me.lucko.luckperms.common.utils.UuidCache;
import me.lucko.luckperms.common.verbose.VerboseHandler;
@ -115,9 +111,9 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private BukkitCommandExecutor commandManager;
private VaultHookManager vaultHookManager = null;
private LuckPermsConfiguration configuration;
private UserManager userManager;
private GroupManager groupManager;
private TrackManager trackManager;
private StandardUserManager userManager;
private StandardGroupManager groupManager;
private StandardTrackManager trackManager;
private Storage storage;
private FileWatcher fileWatcher = null;
private ExtendedMessagingService messagingService = null;
@ -205,7 +201,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
this.scheduler.syncLater(new BukkitProcessorsSetupTask(this), 1L);
// register events
getServer().getPluginManager().registerEvents(new BukkitConnectionListener(this), this);
BukkitConnectionListener connectionListener = new BukkitConnectionListener(this);
getServer().getPluginManager().registerEvents(connectionListener, this);
getServer().getPluginManager().registerEvents(new BukkitPlatformListener(this), this);
if (getConfiguration().get(ConfigKeys.WATCH_FILES)) {
@ -237,9 +234,9 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
// load internal managers
getLog().info("Loading internal permission managers...");
this.uuidCache = new UuidCache(this);
this.userManager = new GenericUserManager(this);
this.groupManager = new GenericGroupManager(this);
this.trackManager = new GenericTrackManager(this);
this.userManager = new StandardUserManager(this);
this.groupManager = new StandardGroupManager(this);
this.trackManager = new StandardTrackManager(this);
this.calculatorFactory = new BukkitCalculatorFactory(this);
this.cachedStateManager = new CachedStateManager();
@ -312,7 +309,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
for (Player player : getServer().getOnlinePlayers()) {
this.scheduler.doAsync(() -> {
try {
LoginHelper.loadUser(this, player.getUniqueId(), player.getName(), false);
connectionListener.loadUser(player.getUniqueId(), player.getName());
User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId()));
if (user != null) {
this.scheduler.doSync(() -> {
@ -584,17 +581,17 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
}
@Override
public UserManager getUserManager() {
public StandardUserManager getUserManager() {
return this.userManager;
}
@Override
public GroupManager getGroupManager() {
public StandardGroupManager getGroupManager() {
return this.groupManager;
}
@Override
public TrackManager getTrackManager() {
public StandardTrackManager getTrackManager() {
return this.trackManager;
}

View File

@ -31,7 +31,7 @@ import me.lucko.luckperms.bukkit.model.PermissibleInjector;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.LoginHelper;
import me.lucko.luckperms.common.utils.AbstractLoginListener;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -47,13 +47,14 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BukkitConnectionListener implements Listener {
public class BukkitConnectionListener extends AbstractLoginListener implements Listener {
private final LPBukkitPlugin plugin;
private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>());
private final Set<UUID> deniedLogin = Collections.synchronizedSet(new HashSet<>());
public BukkitConnectionListener(LPBukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@ -86,7 +87,7 @@ public class BukkitConnectionListener implements Listener {
- creating a user instance in the UserManager for this connection.
- setting up cached data. */
try {
User user = LoginHelper.loadUser(this.plugin, e.getUniqueId(), e.getName(), false);
User user = loadUser(e.getUniqueId(), e.getName());
this.plugin.getEventFactory().handleUserLoginProcess(e.getUniqueId(), e.getName(), user);
} catch (Exception ex) {
this.plugin.getLog().severe("Exception occured whilst loading data for " + e.getUniqueId() + " - " + e.getName());
@ -115,12 +116,6 @@ public class BukkitConnectionListener implements Listener {
return;
}
// Login event was cancelled by another plugin, but it wasn't cancelled when we handled it at LOW
if (e.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
// Schedule cleanup of this user.
this.plugin.getUserManager().scheduleUnload(e.getUniqueId());
}
}
@EventHandler(priority = EventPriority.LOWEST)
@ -180,8 +175,6 @@ public class BukkitConnectionListener implements Listener {
// Login event was cancelled by another plugin since we first loaded their data
if (denied || e.getResult() != PlayerLoginEvent.Result.ALLOWED) {
// Schedule cleanup of this user.
this.plugin.getUserManager().scheduleUnload(e.getPlayer().getUniqueId());
return;
}
@ -206,8 +199,9 @@ public class BukkitConnectionListener implements Listener {
player.setOp(false);
}
// Request that the users data is unloaded.
this.plugin.getUserManager().scheduleUnload(player.getUniqueId());
// Register with the housekeeper, so the User's instance will stick
// around for a bit after they disconnect
this.plugin.getUserManager().getHouseKeeper().registerUsage(player.getUniqueId());
}
}

View File

@ -26,8 +26,11 @@
package me.lucko.luckperms.bungee;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import me.lucko.luckperms.common.utils.SafeIterator;
import net.md_5.bungee.api.scheduler.ScheduledTask;
import net.md_5.bungee.api.scheduler.TaskScheduler;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -35,16 +38,32 @@ 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 LPBungeePlugin plugin;
private final Executor asyncExecutor;
private final Set<ScheduledTask> tasks = ConcurrentHashMap.newKeySet();
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
public BungeeSchedulerAdapter(LPBungeePlugin plugin) {
this.plugin = plugin;
this.asyncExecutor = r -> plugin.getProxy().getScheduler().runAsync(plugin, r);
}
private TaskScheduler scheduler() {
return this.plugin.getProxy().getScheduler();
}
@Override
public Executor async() {
return this.asyncExecutor;
@ -66,30 +85,44 @@ public class BungeeSchedulerAdapter implements SchedulerAdapter {
}
@Override
public void asyncRepeating(Runnable runnable, long intervalTicks) {
long millis = intervalTicks * 50L; // convert from ticks to milliseconds
ScheduledTask task = this.plugin.getProxy().getScheduler().schedule(this.plugin, runnable, millis, millis, TimeUnit.MILLISECONDS);
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
long millis = ticksToMillis(intervalTicks);
SchedulerTask task = new BungeeSchedulerTask(scheduler().schedule(this.plugin, runnable, millis, millis, TimeUnit.MILLISECONDS));
this.tasks.add(task);
return task;
}
@Override
public void syncRepeating(Runnable runnable, long intervalTicks) {
asyncRepeating(runnable, intervalTicks);
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
return asyncRepeating(runnable, intervalTicks);
}
@Override
public void asyncLater(Runnable runnable, long delayTicks) {
long millis = delayTicks * 50L; // convert from ticks to milliseconds
this.plugin.getProxy().getScheduler().schedule(this.plugin, runnable, millis, TimeUnit.MILLISECONDS);
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
return new BungeeSchedulerTask(scheduler().schedule(this.plugin, runnable, ticksToMillis(delayTicks), TimeUnit.MILLISECONDS));
}
@Override
public void syncLater(Runnable runnable, long delayTicks) {
asyncLater(runnable, delayTicks);
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
return asyncLater(runnable, delayTicks);
}
@Override
public void shutdown() {
this.tasks.forEach(ScheduledTask::cancel);
SafeIterator.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();
}
}
}

View File

@ -57,12 +57,9 @@ import me.lucko.luckperms.common.locale.NoopLocaleManager;
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.logging.Logger;
import me.lucko.luckperms.common.logging.SenderLogger;
import me.lucko.luckperms.common.managers.GenericGroupManager;
import me.lucko.luckperms.common.managers.GenericTrackManager;
import me.lucko.luckperms.common.managers.GenericUserManager;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.managers.group.StandardGroupManager;
import me.lucko.luckperms.common.managers.track.StandardTrackManager;
import me.lucko.luckperms.common.managers.user.StandardUserManager;
import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@ -101,9 +98,9 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
private SchedulerAdapter scheduler;
private CommandManager commandManager;
private LuckPermsConfiguration configuration;
private UserManager userManager;
private GroupManager groupManager;
private TrackManager trackManager;
private StandardUserManager userManager;
private StandardGroupManager groupManager;
private StandardTrackManager trackManager;
private Storage storage;
private FileWatcher fileWatcher = null;
private ExtendedMessagingService messagingService = null;
@ -183,9 +180,9 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
// load internal managers
getLog().info("Loading internal permission managers...");
this.uuidCache = new UuidCache(this);
this.userManager = new GenericUserManager(this);
this.groupManager = new GenericGroupManager(this);
this.trackManager = new GenericTrackManager(this);
this.userManager = new StandardUserManager(this);
this.groupManager = new StandardGroupManager(this);
this.trackManager = new StandardTrackManager(this);
this.calculatorFactory = new BungeeCalculatorFactory(this);
this.cachedStateManager = new CachedStateManager();
@ -394,17 +391,17 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
}
@Override
public UserManager getUserManager() {
public StandardUserManager getUserManager() {
return this.userManager;
}
@Override
public GroupManager getGroupManager() {
public StandardGroupManager getGroupManager() {
return this.groupManager;
}
@Override
public TrackManager getTrackManager() {
public StandardTrackManager getTrackManager() {
return this.trackManager;
}

View File

@ -29,7 +29,7 @@ import me.lucko.luckperms.bungee.LPBungeePlugin;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.LoginHelper;
import me.lucko.luckperms.common.utils.AbstractLoginListener;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
@ -43,10 +43,11 @@ import net.md_5.bungee.event.EventPriority;
import java.util.concurrent.TimeUnit;
public class BungeeConnectionListener implements Listener {
public class BungeeConnectionListener extends AbstractLoginListener implements Listener {
private final LPBungeePlugin plugin;
public BungeeConnectionListener(LPBungeePlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@ -82,7 +83,7 @@ public class BungeeConnectionListener implements Listener {
- creating a user instance in the UserManager for this connection.
- setting up cached data. */
try {
User user = LoginHelper.loadUser(this.plugin, c.getUniqueId(), c.getName(), true);
User user = loadUser(c.getUniqueId(), c.getName());
this.plugin.getEventFactory().handleUserLoginProcess(c.getUniqueId(), c.getName(), user);
} catch (Exception ex) {
this.plugin.getLog().severe("Exception occured whilst loading data for " + c.getUniqueId() + " - " + c.getName());
@ -98,10 +99,6 @@ public class BungeeConnectionListener implements Listener {
// finally, complete our intent to modify state, so the proxy can continue handling the connection.
e.completeIntent(this.plugin);
// schedule a cleanup of the users data in a few seconds.
// this should cover the eventuality that the login fails.
this.plugin.getUserManager().scheduleUnload(c.getUniqueId());
});
}
@ -135,8 +132,9 @@ public class BungeeConnectionListener implements Listener {
// Wait until the last priority to unload, so plugins can still perform permission checks on this event
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerQuit(PlayerDisconnectEvent e) {
// Request that the users data is unloaded.
this.plugin.getUserManager().scheduleUnload(e.getPlayer().getUniqueId());
// Register with the housekeeper, so the User's instance will stick
// around for a bit after they disconnect
this.plugin.getUserManager().getHouseKeeper().registerUsage(e.getPlayer().getUniqueId());
}
}

View File

@ -74,7 +74,7 @@ public class LuckPermsApiProvider implements LuckPermsApi {
this.plugin = plugin;
this.platformInfo = new ApiPlatformInfo(plugin);
this.userManager = new ApiUserManager(plugin, plugin.getUserManager());
this.userManager = new ApiUserManager(plugin.getUserManager());
this.groupManager = new ApiGroupManager(plugin.getGroupManager());
this.trackManager = new ApiTrackManager(plugin.getTrackManager());
this.actionLogger = new ApiActionLogger(plugin);

View File

@ -35,9 +35,9 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull;
public class ApiGroupManager implements GroupManager {
private final me.lucko.luckperms.common.managers.GroupManager handle;
private final me.lucko.luckperms.common.managers.group.GroupManager<?> handle;
public ApiGroupManager(me.lucko.luckperms.common.managers.GroupManager handle) {
public ApiGroupManager(me.lucko.luckperms.common.managers.group.GroupManager<?> handle) {
this.handle = handle;
}

View File

@ -35,9 +35,9 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull;
public class ApiTrackManager implements TrackManager {
private final me.lucko.luckperms.common.managers.TrackManager handle;
private final me.lucko.luckperms.common.managers.track.TrackManager<?> handle;
public ApiTrackManager(me.lucko.luckperms.common.managers.TrackManager handle) {
public ApiTrackManager(me.lucko.luckperms.common.managers.track.TrackManager<?> handle) {
this.handle = handle;
}

View File

@ -28,7 +28,6 @@ package me.lucko.luckperms.common.api.delegates.manager;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.manager.UserManager;
import me.lucko.luckperms.common.api.delegates.model.ApiUser;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.references.UserIdentifier;
import java.util.Objects;
@ -39,11 +38,9 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull;
public class ApiUserManager implements UserManager {
private final LuckPermsPlugin plugin;
private final me.lucko.luckperms.common.managers.UserManager handle;
private final me.lucko.luckperms.common.managers.user.UserManager<?> handle;
public ApiUserManager(LuckPermsPlugin plugin, me.lucko.luckperms.common.managers.UserManager handle) {
this.plugin = plugin;
public ApiUserManager(me.lucko.luckperms.common.managers.user.UserManager<?> handle) {
this.handle = handle;
}
@ -76,6 +73,6 @@ public class ApiUserManager implements UserManager {
@Override
public void cleanupUser(@Nonnull User user) {
Objects.requireNonNull(user, "user");
this.handle.scheduleUnload(this.plugin.getUuidCache().getExternalUUID(ApiUser.cast(user).getUuid()));
this.handle.getHouseKeeper().clearApiUsage(ApiUser.cast(user).getUuid());
}
}

View File

@ -99,6 +99,11 @@ public class ApiStorage implements Storage {
@Override
public CompletableFuture<Boolean> loadUser(@Nonnull UUID uuid, String username) {
Objects.requireNonNull(uuid, "uuid");
if (this.plugin.getUserManager().getIfLoaded(uuid) == null) {
this.plugin.getUserManager().getHouseKeeper().registerApiUsage(uuid);
}
return this.handle.noBuffer().loadUser(uuid, username == null ? null : checkUsername(username)).thenApply(Objects::nonNull);
}

View File

@ -40,9 +40,10 @@ import javax.annotation.Nonnull;
* An abstract manager class
*
* @param <I> the class used to identify each object held in this manager
* @param <T> the class this manager is "managing"
* @param <C> the super class being managed
* @param <T> the implementation class this manager is "managing"
*/
public abstract class AbstractManager<I, T extends Identifiable<I>> implements Manager<I, T> {
public abstract class AbstractManager<I, C extends Identifiable<I>, T extends C> implements Manager<I, C, T> {
private final LoadingCache<I, T> objects = Caffeine.newBuilder()
.build(new CacheLoader<I, T>() {
@ -85,9 +86,9 @@ public abstract class AbstractManager<I, T extends Identifiable<I>> implements M
}
@Override
public void unload(T t) {
if (t != null) {
unload(t.getId());
public void unload(C object) {
if (object != null) {
unload(object.getId());
}
}

View File

@ -34,16 +34,17 @@ import java.util.function.Function;
* A class which manages instances of a class
*
* @param <I> the class used to identify each object held in this manager
* @param <T> the class this manager is "managing"
* @param <C> the super class being managed
* @param <T> the implementation class this manager is "managing"
*/
public interface Manager<I, T extends Identifiable<I>> extends Function<I, T> {
public interface Manager<I, C extends Identifiable<I>, T extends C> extends Function<I, T> {
/**
* Gets a map containing all cached instances held by this manager.
*
* @return all instances held in this manager
*/
Map<I, ? extends T> getAll();
Map<I, T> getAll();
/**
* Gets or creates an object by id
@ -81,9 +82,9 @@ public interface Manager<I, T extends Identifiable<I>> extends Function<I, T> {
/**
* Removes and unloads the object from the manager
*
* @param t The object to unload
* @param object The object to unload
*/
void unload(T t);
void unload(C object);
/**
* Unloads all objects from the manager

View File

@ -23,40 +23,30 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.managers;
package me.lucko.luckperms.common.managers.group;
import me.lucko.luckperms.common.managers.AbstractManager;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
public class GenericGroupManager extends AbstractManager<String, Group> implements GroupManager {
private final LuckPermsPlugin plugin;
public GenericGroupManager(LuckPermsPlugin plugin) {
this.plugin = plugin;
}
public abstract class AbstractGroupManager<T extends Group> extends AbstractManager<String, Group, T> implements GroupManager<T> {
@Override
public Group apply(String name) {
return new Group(name, this.plugin);
}
@Override
public Group getByDisplayName(String name) {
public T getByDisplayName(String name) {
// try to get an exact match first
Group g = getIfLoaded(name);
T g = getIfLoaded(name);
if (g != null) {
return g;
}
// then try exact display name matches
for (Group group : getAll().values()) {
for (T group : getAll().values()) {
if (group.getDisplayName().isPresent() && group.getDisplayName().get().equals(name)) {
return group;
}
}
// then try case insensitive name matches
for (Group group : getAll().values()) {
for (T group : getAll().values()) {
if (group.getDisplayName().isPresent() && group.getDisplayName().get().equalsIgnoreCase(name)) {
return group;
}

View File

@ -23,11 +23,12 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.managers;
package me.lucko.luckperms.common.managers.group;
import me.lucko.luckperms.common.managers.Manager;
import me.lucko.luckperms.common.model.Group;
public interface GroupManager extends Manager<String, Group> {
public interface GroupManager<T extends Group> extends Manager<String, Group, T> {
/**
* Get a group object by display name
@ -35,6 +36,6 @@ public interface GroupManager extends Manager<String, Group> {
* @param name The name to search by
* @return a {@link Group} object if the group is loaded, returns null if the group is not loaded
*/
Group getByDisplayName(String name);
T getByDisplayName(String name);
}

View File

@ -0,0 +1,42 @@
/*
* 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.managers.group;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
public class StandardGroupManager extends AbstractGroupManager<Group> {
private final LuckPermsPlugin plugin;
public StandardGroupManager(LuckPermsPlugin plugin) {
this.plugin = plugin;
}
@Override
public Group apply(String name) {
return new Group(name, this.plugin);
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.managers.track;
import me.lucko.luckperms.common.managers.AbstractManager;
import me.lucko.luckperms.common.model.Track;
public abstract class AbstractTrackManager<T extends Track> extends AbstractManager<String, Track, T> implements TrackManager<T> {
@Override
protected String sanitizeIdentifier(String s) {
return s.toLowerCase();
}
}

View File

@ -23,15 +23,15 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.managers;
package me.lucko.luckperms.common.managers.track;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
public class GenericTrackManager extends AbstractManager<String, Track> implements TrackManager {
public class StandardTrackManager extends AbstractTrackManager<Track> {
private final LuckPermsPlugin plugin;
public GenericTrackManager(LuckPermsPlugin plugin) {
public StandardTrackManager(LuckPermsPlugin plugin) {
this.plugin = plugin;
}
@ -39,9 +39,4 @@ public class GenericTrackManager extends AbstractManager<String, Track> implemen
public Track apply(String name) {
return new Track(name, this.plugin);
}
@Override
protected String sanitizeIdentifier(String s) {
return s.toLowerCase();
}
}

View File

@ -23,10 +23,11 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.managers;
package me.lucko.luckperms.common.managers.track;
import me.lucko.luckperms.common.managers.Manager;
import me.lucko.luckperms.common.model.Track;
public interface TrackManager extends Manager<String, Track> {
public interface TrackManager<T extends Track> extends Manager<String, Track, T> {
}

View File

@ -23,11 +23,12 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.managers;
package me.lucko.luckperms.common.managers.user;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.managers.AbstractManager;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@ -37,17 +38,20 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class GenericUserManager extends AbstractManager<UserIdentifier, User> implements UserManager {
public abstract class AbstractUserManager<T extends User> extends AbstractManager<UserIdentifier, User, T> implements UserManager<T> {
private final LuckPermsPlugin plugin;
private final UserHousekeeper housekeeper;
public GenericUserManager(LuckPermsPlugin plugin) {
public AbstractUserManager(LuckPermsPlugin plugin, UserHousekeeper.TimeoutSettings timeoutSettings) {
this.plugin = plugin;
this.housekeeper = new UserHousekeeper(plugin, this, timeoutSettings);
this.plugin.getScheduler().asyncRepeating(this.housekeeper, 200L); // every 10 seconds
}
@Override
public User getOrMake(UserIdentifier id) {
User ret = super.getOrMake(id);
public T getOrMake(UserIdentifier id) {
T ret = super.getOrMake(id);
if (id.getUsername().isPresent()) {
ret.setName(id.getUsername().get(), false);
}
@ -55,15 +59,8 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
}
@Override
public User apply(UserIdentifier id) {
return !id.getUsername().isPresent() ?
new User(id.getUuid(), this.plugin) :
new User(id.getUuid(), id.getUsername().get(), this.plugin);
}
@Override
public User getByUsername(String name) {
for (User user : getAll().values()) {
public T getByUsername(String name) {
for (T user : getAll().values()) {
Optional<String> n = user.getName();
if (n.isPresent() && n.get().equalsIgnoreCase(name)) {
return user;
@ -73,59 +70,16 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
}
@Override
public User getIfLoaded(UUID uuid) {
public T getIfLoaded(UUID uuid) {
return getIfLoaded(UserIdentifier.of(uuid, null));
}
@Override
public boolean giveDefaultIfNeeded(User user, boolean save) {
return giveDefaultIfNeeded(user, save, this.plugin);
}
@Override
public boolean cleanup(User user) {
if (!this.plugin.isPlayerOnline(this.plugin.getUuidCache().getExternalUUID(user.getUuid()))) {
unload(user);
return true;
} else {
return false;
}
}
@Override
public void scheduleUnload(UUID uuid) {
this.plugin.getScheduler().asyncLater(() -> {
// check once to see if the user can be unloaded.
if (getIfLoaded(this.plugin.getUuidCache().getUUID(uuid)) != null && !this.plugin.isPlayerOnline(uuid)) {
// check again in 40 ticks, we want to be sure the player won't have re-logged before we unload them.
this.plugin.getScheduler().asyncLater(() -> {
User user = getIfLoaded(this.plugin.getUuidCache().getUUID(uuid));
if (user != null && !this.plugin.isPlayerOnline(uuid)) {
user.getCachedData().invalidateCaches();
unload(user);
this.plugin.getUuidCache().clearCache(uuid);
}
}, 40L);
}
}, 40L);
}
@Override
public CompletableFuture<Void> updateAllUsers() {
return CompletableFuture.runAsync(
() -> this.plugin.getOnlinePlayers()
.map(u -> this.plugin.getUuidCache().getUUID(u))
.forEach(u -> this.plugin.getStorage().loadUser(u, null).join()),
this.plugin.getScheduler().async()
);
}
public static boolean giveDefaultIfNeeded(User user, boolean save, LuckPermsPlugin plugin) {
boolean work = false;
// check that they are actually a member of their primary group, otherwise remove it
if (plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION_METHOD).equals("stored")) {
if (this.plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION_METHOD).equals("stored")) {
String pg = user.getPrimaryGroup().getValue();
boolean has = false;
@ -174,19 +128,40 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
}
if (work && save) {
plugin.getStorage().saveUser(user);
this.plugin.getStorage().saveUser(user);
}
return work;
}
@Override
public UserHousekeeper getHouseKeeper() {
return this.housekeeper;
}
@Override
public void cleanup(User user) {
this.housekeeper.cleanup(user.getId());
}
@Override
public CompletableFuture<Void> updateAllUsers() {
return CompletableFuture.runAsync(
() -> this.plugin.getOnlinePlayers()
.map(u -> this.plugin.getUuidCache().getUUID(u))
.forEach(u -> this.plugin.getStorage().loadUser(u, null).join()),
this.plugin.getScheduler().async()
);
}
/**
* Check whether the user's state indicates that they should be persisted to storage.
*
* @param user the user to check
* @return true if the user should be saved
*/
public static boolean shouldSave(User user) {
@Override
public boolean shouldSave(User user) {
if (user.getEnduringNodes().size() != 1) {
return true;
}

View File

@ -0,0 +1,48 @@
/*
* 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.managers.user;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.references.UserIdentifier;
import java.util.concurrent.TimeUnit;
public class StandardUserManager extends AbstractUserManager<User> {
private final LuckPermsPlugin plugin;
public StandardUserManager(LuckPermsPlugin plugin) {
super(plugin, UserHousekeeper.timeoutSettings(1, TimeUnit.MINUTES));
this.plugin = plugin;
}
@Override
public User apply(UserIdentifier id) {
return !id.getUsername().isPresent() ?
new User(id.getUuid(), this.plugin) :
new User(id.getUuid(), id.getUsername().get(), this.plugin);
}
}

View File

@ -0,0 +1,100 @@
/*
* 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.managers.user;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.references.UserIdentifier;
import me.lucko.luckperms.common.utils.ExpiringSet;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* The instance responsible for unloading users which are no longer needed.
*/
public class UserHousekeeper implements Runnable {
private final LuckPermsPlugin plugin;
private final UserManager<?> userManager;
// contains the uuids of users who have recently logged in / out
private final ExpiringSet<UUID> recentlyUsed;
// contains the uuids of users who have recently been retrieved from the API
private final ExpiringSet<UUID> recentlyUsedApi;
public UserHousekeeper(LuckPermsPlugin plugin, UserManager<?> userManager, TimeoutSettings timeoutSettings) {
this.plugin = plugin;
this.userManager = userManager;
this.recentlyUsed = new ExpiringSet<>(timeoutSettings.duration, timeoutSettings.unit);
this.recentlyUsedApi = new ExpiringSet<>(5, TimeUnit.MINUTES);
}
// called when a player attempts a connection or logs out
public void registerUsage(UUID uuid) {
this.recentlyUsed.add(uuid);
}
public void registerApiUsage(UUID uuid) {
this.recentlyUsedApi.add(uuid);
}
public void clearApiUsage(UUID uuid) {
this.recentlyUsedApi.remove(uuid);
}
@Override
public void run() {
for (UserIdentifier entry : this.userManager.getAll().keySet()) {
cleanup(entry);
}
}
public void cleanup(UserIdentifier identifier) {
UUID uuid = identifier.getUuid();
// unload users which aren't online and who haven't been online (or tried to login) recently
if (this.recentlyUsed.contains(uuid) || this.recentlyUsedApi.contains(uuid) || this.plugin.isPlayerOnline(this.plugin.getUuidCache().getExternalUUID(uuid))) {
return;
}
// unload them
this.userManager.unload(identifier);
}
public static TimeoutSettings timeoutSettings(long duration, TimeUnit unit) {
return new TimeoutSettings(duration, unit);
}
public static final class TimeoutSettings {
private final long duration;
private final TimeUnit unit;
private TimeoutSettings(long duration, TimeUnit unit) {
this.duration = duration;
this.unit = unit;
}
}
}

View File

@ -23,16 +23,16 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.managers;
package me.lucko.luckperms.common.managers.user;
import me.lucko.luckperms.common.managers.Manager;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.references.Identifiable;
import me.lucko.luckperms.common.references.UserIdentifier;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public interface UserManager extends Manager<UserIdentifier, User> {
public interface UserManager<T extends User> extends Manager<UserIdentifier, User, T> {
/**
* Get a user object by name
@ -40,7 +40,7 @@ public interface UserManager extends Manager<UserIdentifier, User> {
* @param name The name to search by
* @return a {@link User} object if the user is loaded, returns null if the user is not loaded
*/
User getByUsername(String name);
T getByUsername(String name);
/**
* Get a user object by uuid
@ -48,7 +48,7 @@ public interface UserManager extends Manager<UserIdentifier, User> {
* @param uuid The uuid to search by
* @return a {@link User} object if the user is loaded, returns null if the user is not loaded
*/
User getIfLoaded(UUID uuid);
T getIfLoaded(UUID uuid);
/**
* Gives the user the default group if necessary.
@ -58,18 +58,26 @@ public interface UserManager extends Manager<UserIdentifier, User> {
boolean giveDefaultIfNeeded(User user, boolean save);
/**
* Checks to see if the user is online, and if they are not, runs {@link #unload(Identifiable)}
* Check whether the user's state indicates that they should be persisted to storage.
*
* @param user The user to be cleaned up
* @param user the user to check
* @return true if the user should be saved
*/
boolean cleanup(User user);
boolean shouldSave(User user);
/**
* Schedules a task to cleanup a user after a certain period of time, if they're not on the server anymore.
* Gets the instance responsible for unloading unneeded users.
*
* @param uuid external uuid of the player
* @return the housekeeper
*/
void scheduleUnload(UUID uuid);
UserHousekeeper getHouseKeeper();
/**
* Unloads the user if a corresponding player is not online
*
* @param user the user
*/
void cleanup(User user);
/**
* Reloads the data of all online users

View File

@ -42,9 +42,9 @@ import me.lucko.luckperms.common.dependencies.DependencyManager;
import me.lucko.luckperms.common.event.EventFactory;
import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.logging.Logger;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.managers.group.GroupManager;
import me.lucko.luckperms.common.managers.track.TrackManager;
import me.lucko.luckperms.common.managers.user.UserManager;
import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.storage.Storage;
@ -75,21 +75,21 @@ public interface LuckPermsPlugin {
*
* @return the user manager
*/
UserManager getUserManager();
UserManager<?> getUserManager();
/**
* Gets the group manager instance for the platform
*
* @return the group manager
*/
GroupManager getGroupManager();
GroupManager<?> getGroupManager();
/**
* Gets the track manager instance for the platform
*
* @return the track manager
*/
TrackManager getTrackManager();
TrackManager<?> getTrackManager();
/**
* Gets the plugin's configuration

View File

@ -65,28 +65,28 @@ public interface SchedulerAdapter {
* @param runnable the runnable
* @param intervalTicks the interval in ticks.
*/
void asyncRepeating(Runnable runnable, long intervalTicks);
SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks);
/**
* 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.
*/
void syncRepeating(Runnable runnable, long intervalTicks);
SchedulerTask syncRepeating(Runnable runnable, long intervalTicks);
/**
* Runs a runnable with a delay
* @param runnable the runnable
* @param delayTicks the delay in ticks
*/
void asyncLater(Runnable runnable, long delayTicks);
SchedulerTask asyncLater(Runnable runnable, long delayTicks);
/**
* Runs a runnable with a delay
* @param runnable the runnable
* @param delayTicks the delay in ticks
*/
void syncLater(Runnable runnable, long delayTicks);
SchedulerTask syncLater(Runnable runnable, long delayTicks);
/**
* Shuts down this executor instance

View File

@ -0,0 +1,38 @@
/*
* 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;
/**
* Represents a scheduled task
*/
public interface SchedulerTask {
/**
* Cancels the task.
*/
void cancel();
}

View File

@ -37,9 +37,8 @@ import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.commands.CommandManager;
import me.lucko.luckperms.common.commands.utils.CommandUtils;
import me.lucko.luckperms.common.contexts.ContextSetConfigurateSerializer;
import me.lucko.luckperms.common.managers.GenericUserManager;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.group.GroupManager;
import me.lucko.luckperms.common.managers.track.TrackManager;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User;
@ -400,7 +399,7 @@ public abstract class ConfigurateDao extends AbstractDao {
saveUser(user);
}
} else {
if (GenericUserManager.shouldSave(user)) {
if (this.plugin.getUserManager().shouldSave(user)) {
user.clearNodes();
user.getPrimaryGroup().setStoredValue(null);
this.plugin.getUserManager().giveDefaultIfNeeded(user, false);
@ -419,7 +418,7 @@ public abstract class ConfigurateDao extends AbstractDao {
public void saveUser(User user) throws Exception {
user.getIoLock().lock();
try {
if (!GenericUserManager.shouldSave(user)) {
if (!this.plugin.getUserManager().shouldSave(user)) {
saveFile(StorageLocation.USER, user.getUuid().toString(), null);
} else {
ConfigurationNode data = SimpleConfigurationNode.root();
@ -562,7 +561,7 @@ public abstract class ConfigurateDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a group");
}
GroupManager gm = this.plugin.getGroupManager();
GroupManager<?> gm = this.plugin.getGroupManager();
gm.getAll().values().stream()
.filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload);
@ -718,7 +717,7 @@ public abstract class ConfigurateDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a track");
}
TrackManager tm = this.plugin.getTrackManager();
TrackManager<?> tm = this.plugin.getTrackManager();
tm.getAll().values().stream()
.filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload);

View File

@ -44,9 +44,8 @@ import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.actionlog.ExtendedLogEntry;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.managers.GenericUserManager;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.group.GroupManager;
import me.lucko.luckperms.common.managers.track.TrackManager;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User;
@ -268,7 +267,7 @@ public class MongoDao extends AbstractDao {
c.replaceOne(new Document("_id", user.getUuid()), userToDoc(user));
}
} else {
if (GenericUserManager.shouldSave(user)) {
if (this.plugin.getUserManager().shouldSave(user)) {
user.clearNodes();
user.getPrimaryGroup().setStoredValue(null);
this.plugin.getUserManager().giveDefaultIfNeeded(user, false);
@ -287,7 +286,7 @@ public class MongoDao extends AbstractDao {
user.getIoLock().lock();
try {
MongoCollection<Document> c = this.database.getCollection(this.prefix + "users");
if (!GenericUserManager.shouldSave(user)) {
if (!this.plugin.getUserManager().shouldSave(user)) {
c.deleteOne(new Document("_id", user.getUuid()));
} else {
c.replaceOne(new Document("_id", user.getUuid()), userToDoc(user), new UpdateOptions().upsert(true));
@ -409,7 +408,7 @@ public class MongoDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a group");
}
GroupManager gm = this.plugin.getGroupManager();
GroupManager<?> gm = this.plugin.getGroupManager();
gm.getAll().values().stream()
.filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload);
@ -535,7 +534,7 @@ public class MongoDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a track");
}
TrackManager tm = this.plugin.getTrackManager();
TrackManager<?> tm = this.plugin.getTrackManager();
tm.getAll().values().stream()
.filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload);

View File

@ -37,9 +37,8 @@ import me.lucko.luckperms.common.actionlog.ExtendedLogEntry;
import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.contexts.ContextSetJsonSerializer;
import me.lucko.luckperms.common.managers.GenericUserManager;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.group.GroupManager;
import me.lucko.luckperms.common.managers.track.TrackManager;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User;
@ -364,7 +363,7 @@ public class SqlDao extends AbstractDao {
} else {
// User has no data in storage.
if (GenericUserManager.shouldSave(user)) {
if (this.plugin.getUserManager().shouldSave(user)) {
user.clearNodes();
user.getPrimaryGroup().setStoredValue(null);
this.plugin.getUserManager().giveDefaultIfNeeded(user, false);
@ -382,7 +381,7 @@ public class SqlDao extends AbstractDao {
user.getIoLock().lock();
try {
// Empty data - just delete from the DB.
if (!GenericUserManager.shouldSave(user)) {
if (!this.plugin.getUserManager().shouldSave(user)) {
try (Connection c = this.provider.getConnection()) {
try (PreparedStatement ps = c.prepareStatement(this.prefix.apply(USER_PERMISSIONS_DELETE))) {
ps.setString(1, user.getUuid().toString());
@ -643,7 +642,7 @@ public class SqlDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a group");
}
GroupManager gm = this.plugin.getGroupManager();
GroupManager<?> gm = this.plugin.getGroupManager();
gm.getAll().values().stream()
.filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload);
@ -880,7 +879,7 @@ public class SqlDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a track");
}
TrackManager tm = this.plugin.getTrackManager();
TrackManager<?> tm = this.plugin.getTrackManager();
tm.getAll().values().stream()
.filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload);

View File

@ -25,6 +25,7 @@
package me.lucko.luckperms.common.utils;
import me.lucko.luckperms.api.platform.PlatformType;
import me.lucko.luckperms.common.assignments.AssignmentRule;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User;
@ -34,47 +35,61 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
* Utilities for use in platform listeners
* Abstract listener utility for handling new player connections
*/
public final class LoginHelper {
public abstract class AbstractLoginListener {
private final LuckPermsPlugin plugin;
public static User loadUser(LuckPermsPlugin plugin, UUID u, String username, boolean joinUuidSave) {
// if we should #join the uuid save future.
// this is only really necessary on BungeeCord, as the data may be needed
// on the backend, depending on uuid config options
private final boolean joinUuidSave;
protected AbstractLoginListener(LuckPermsPlugin plugin) {
this.plugin = plugin;
this.joinUuidSave = plugin.getServerType() == PlatformType.BUNGEE;
}
public User loadUser(UUID u, String username) {
final long startTime = System.currentTimeMillis();
final UuidCache cache = plugin.getUuidCache();
if (!plugin.getConfiguration().get(ConfigKeys.USE_SERVER_UUIDS)) {
UUID uuid = plugin.getStorage().noBuffer().getUUID(username).join();
// register with the housekeeper to avoid accidental unloads
this.plugin.getUserManager().getHouseKeeper().registerUsage(u);
final UuidCache cache = this.plugin.getUuidCache();
if (!this.plugin.getConfiguration().get(ConfigKeys.USE_SERVER_UUIDS)) {
UUID uuid = this.plugin.getStorage().noBuffer().getUUID(username).join();
if (uuid != null) {
cache.addToCache(u, uuid);
} else {
// No previous data for this player
plugin.getEventFactory().handleUserFirstLogin(u, username);
this.plugin.getEventFactory().handleUserFirstLogin(u, username);
cache.addToCache(u, u);
CompletableFuture<Void> future = plugin.getStorage().noBuffer().saveUUIDData(u, username);
if (joinUuidSave) {
CompletableFuture<Void> future = this.plugin.getStorage().noBuffer().saveUUIDData(u, username);
if (this.joinUuidSave) {
future.join();
}
}
} else {
String name = plugin.getStorage().noBuffer().getName(u).join();
String name = this.plugin.getStorage().noBuffer().getName(u).join();
if (name == null) {
plugin.getEventFactory().handleUserFirstLogin(u, username);
this.plugin.getEventFactory().handleUserFirstLogin(u, username);
}
// Online mode, no cache needed. This is just for name -> uuid lookup.
CompletableFuture<Void> future = plugin.getStorage().noBuffer().saveUUIDData(u, username);
if (joinUuidSave) {
CompletableFuture<Void> future = this.plugin.getStorage().noBuffer().saveUUIDData(u, username);
if (this.joinUuidSave) {
future.join();
}
}
User user = plugin.getStorage().noBuffer().loadUser(cache.getUUID(u), username).join();
User user = this.plugin.getStorage().noBuffer().loadUser(cache.getUUID(u), username).join();
if (user == null) {
throw new NullPointerException("User is null");
} else {
// Setup defaults for the user
boolean save = false;
for (AssignmentRule rule : plugin.getConfiguration().get(ConfigKeys.DEFAULT_ASSIGNMENTS)) {
for (AssignmentRule rule : this.plugin.getConfiguration().get(ConfigKeys.DEFAULT_ASSIGNMENTS)) {
if (rule.apply(user)) {
save = true;
}
@ -82,7 +97,7 @@ public final class LoginHelper {
// If they were given a default, persist the new assignments back to the storage.
if (save) {
plugin.getStorage().noBuffer().saveUser(user).join();
this.plugin.getStorage().noBuffer().saveUser(user).join();
}
// Does some minimum pre-calculations to (maybe) speed things up later.
@ -91,12 +106,10 @@ public final class LoginHelper {
final long time = System.currentTimeMillis() - startTime;
if (time >= 1000) {
plugin.getLog().warn("Processing login for " + username + " took " + time + "ms.");
this.plugin.getLog().warn("Processing login for " + username + " took " + time + "ms.");
}
return user;
}
private LoginHelper() {}
}

View File

@ -0,0 +1,90 @@
/*
* 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 com.google.common.collect.ForwardingSet;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
/**
* A bad 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 Set<E> setView;
public ExpiringSet(long duration, TimeUnit unit) {
this.cache = Caffeine.newBuilder().expireAfterAccess(duration, unit).build(key -> Boolean.TRUE);
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.
// we don't care about the return value
return true;
}
@Override
public boolean addAll(@Nonnull Collection<? extends E> collection) {
for (E element : collection) {
add(element);
}
// we don't care about the return value
return true;
}
@Override
public boolean remove(Object key) {
this.cache.invalidate(key);
// we don't care about the return value
return true;
}
@Override
public boolean removeAll(@Nonnull Collection<?> keys) {
this.cache.invalidateAll(keys);
// we don't care about the return value
return true;
}
@Override
protected Set<E> delegate() {
return this.setView;
}
}

View File

@ -52,8 +52,7 @@ import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.locale.NoopLocaleManager;
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.logging.SenderLogger;
import me.lucko.luckperms.common.managers.GenericTrackManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.track.StandardTrackManager;
import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@ -168,7 +167,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
private LuckPermsConfiguration configuration;
private SpongeUserManager userManager;
private SpongeGroupManager groupManager;
private TrackManager trackManager;
private StandardTrackManager trackManager;
private Storage storage;
private FileWatcher fileWatcher = null;
private ExtendedMessagingService messagingService = null;
@ -243,7 +242,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
this.uuidCache = new UuidCache(this);
this.userManager = new SpongeUserManager(this);
this.groupManager = new SpongeGroupManager(this);
this.trackManager = new GenericTrackManager(this);
this.trackManager = new StandardTrackManager(this);
this.calculatorFactory = new SpongeCalculatorFactory(this);
this.cachedStateManager = new CachedStateManager();
@ -561,7 +560,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
}
@Override
public TrackManager getTrackManager() {
public StandardTrackManager getTrackManager() {
return this.trackManager;
}

View File

@ -26,7 +26,10 @@
package me.lucko.luckperms.sponge;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import me.lucko.luckperms.common.plugin.SchedulerTask;
import me.lucko.luckperms.common.utils.SafeIterator;
import org.spongepowered.api.scheduler.Scheduler;
import org.spongepowered.api.scheduler.Task;
import java.util.Set;
@ -35,12 +38,16 @@ import java.util.concurrent.Executor;
public class SpongeSchedulerAdapter implements SchedulerAdapter {
private final LPSpongePlugin plugin;
private final Set<Task> tasks = ConcurrentHashMap.newKeySet();
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
public SpongeSchedulerAdapter(LPSpongePlugin plugin) {
this.plugin = plugin;
}
private Scheduler scheduler() {
return this.plugin.getSpongeScheduler();
}
@Override
public Executor async() {
return this.plugin.getAsyncExecutorService();
@ -62,29 +69,72 @@ public class SpongeSchedulerAdapter implements SchedulerAdapter {
}
@Override
public void asyncRepeating(Runnable runnable, long intervalTicks) {
Task task = this.plugin.getSpongeScheduler().createTaskBuilder().async().intervalTicks(intervalTicks).delayTicks(intervalTicks).execute(runnable).submit(this.plugin);
this.tasks.add(task);
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
Task task = scheduler().createTaskBuilder()
.async()
.intervalTicks(intervalTicks)
.delayTicks(intervalTicks)
.execute(runnable)
.submit(this.plugin);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
}
@Override
public void syncRepeating(Runnable runnable, long intervalTicks) {
Task task = this.plugin.getSpongeScheduler().createTaskBuilder().intervalTicks(intervalTicks).delayTicks(intervalTicks).execute(runnable).submit(this.plugin);
this.tasks.add(task);
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
Task task = scheduler().createTaskBuilder()
.intervalTicks(intervalTicks)
.delayTicks(intervalTicks)
.execute(runnable)
.submit(this.plugin);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
}
@Override
public void asyncLater(Runnable runnable, long delayTicks) {
this.plugin.getSpongeScheduler().createTaskBuilder().async().delayTicks(delayTicks).execute(runnable).submit(this.plugin);
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
Task task = scheduler().createTaskBuilder()
.async()
.delayTicks(delayTicks)
.execute(runnable)
.submit(this.plugin);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
}
@Override
public void syncLater(Runnable runnable, long delayTicks) {
this.plugin.getSpongeScheduler().createTaskBuilder().delayTicks(delayTicks).execute(runnable).submit(this.plugin);
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
Task task = scheduler().createTaskBuilder()
.delayTicks(delayTicks)
.execute(runnable)
.submit(this.plugin);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
}
@Override
public void shutdown() {
this.tasks.forEach(Task::cancel);
SafeIterator.iterate(this.tasks, SchedulerTask::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.listeners;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.LoginHelper;
import me.lucko.luckperms.common.utils.AbstractLoginListener;
import me.lucko.luckperms.common.utils.UuidCache;
import me.lucko.luckperms.sponge.LPSpongePlugin;
@ -45,13 +45,14 @@ import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class SpongeConnectionListener {
public class SpongeConnectionListener extends AbstractLoginListener {
private final LPSpongePlugin plugin;
private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>());
private final Set<UUID> deniedLogin = Collections.synchronizedSet(new HashSet<>());
public SpongeConnectionListener(LPSpongePlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@ -80,7 +81,7 @@ public class SpongeConnectionListener {
- creating a user instance in the UserManager for this connection.
- setting up cached data. */
try {
User user = LoginHelper.loadUser(this.plugin, p.getUniqueId(), username, false);
User user = loadUser(p.getUniqueId(), username);
this.plugin.getEventFactory().handleUserLoginProcess(p.getUniqueId(), username, user);
} catch (Exception ex) {
this.plugin.getLog().severe("Exception occured whilst loading data for " + p.getUniqueId() + " - " + p.getName());
@ -165,6 +166,10 @@ public class SpongeConnectionListener {
// Unload the user from memory when they disconnect
cache.clearCache(e.getTargetEntity().getUniqueId());
// Register with the housekeeper, so the User's instance will stick
// around for a bit after they disconnect
this.plugin.getUserManager().getHouseKeeper().registerUsage(e.getTargetEntity().getUniqueId());
}
}

View File

@ -36,8 +36,7 @@ import me.lucko.luckperms.api.HeldPermission;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.event.cause.CreationCause;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.managers.group.AbstractGroupManager;
import me.lucko.luckperms.common.storage.DataConstraints;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.LPSpongePlugin;
@ -61,15 +60,10 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
public class SpongeGroupManager extends AbstractGroupManager<SpongeGroup> implements LPSubjectCollection {
private final LPSpongePlugin plugin;
private SubjectCollection spongeProxy = null;
private final LoadingCache<String, SpongeGroup> objects = Caffeine.newBuilder()
.build(this::apply);
private final LoadingCache<String, LPSubject> subjectLoadingCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build(s -> {
@ -104,53 +98,6 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
return new SpongeGroup(name, this.plugin);
}
/* ------------------------------------------
* Manager methods
* ------------------------------------------ */
@Override
public Map<String, SpongeGroup> getAll() {
return ImmutableMap.copyOf(this.objects.asMap());
}
@Override
public SpongeGroup getOrMake(String id) {
return this.objects.get(id.toLowerCase());
}
@Override
public SpongeGroup getIfLoaded(String id) {
return this.objects.getIfPresent(id.toLowerCase());
}
@Override
public boolean isLoaded(String id) {
return this.objects.asMap().containsKey(id.toLowerCase());
}
@Override
public void unload(String id) {
if (id != null) {
this.objects.invalidate(id.toLowerCase());
}
}
@Override
public void unload(Group t) {
if (t != null) {
unload(t.getId());
}
}
@Override
public void unloadAll() {
this.objects.invalidateAll();
}
/* ------------------------------------------
* SubjectCollection methods
* ------------------------------------------ */
@Override
public synchronized SubjectCollection sponge() {
if (this.spongeProxy == null) {
@ -160,6 +107,10 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
return this.spongeProxy;
}
public LPSpongePlugin getPlugin() {
return this.plugin;
}
@Override
public LuckPermsService getService() {
return this.plugin.getService();
@ -261,7 +212,7 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
@Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) {
return this.objects.asMap().values().stream()
return getAll().values().stream()
.map(SpongeGroup::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
@ -270,7 +221,7 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
@Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) {
return this.objects.asMap().values().stream()
return getAll().values().stream()
.map(SpongeGroup::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
@ -282,33 +233,4 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
return getService().getDefaultSubjects().loadSubject(getIdentifier()).join();
}
@Override
public Group getByDisplayName(String name) {
// try to get an exact match first
Group g = getIfLoaded(name);
if (g != null) {
return g;
}
// then try exact display name matches
for (Group group : getAll().values()) {
if (group.getDisplayName().isPresent() && group.getDisplayName().get().equals(name)) {
return group;
}
}
// then try case insensitive name matches
for (Group group : getAll().values()) {
if (group.getDisplayName().isPresent() && group.getDisplayName().get().equalsIgnoreCase(name)) {
return group;
}
}
return null;
}
public LPSpongePlugin getPlugin() {
return this.plugin;
}
}

View File

@ -35,9 +35,8 @@ import com.google.common.collect.Maps;
import me.lucko.luckperms.api.HeldPermission;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.managers.GenericUserManager;
import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.managers.user.AbstractUserManager;
import me.lucko.luckperms.common.managers.user.UserHousekeeper;
import me.lucko.luckperms.common.references.UserIdentifier;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.LPSpongePlugin;
@ -62,18 +61,16 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
public class SpongeUserManager implements UserManager, LPSubjectCollection {
public class SpongeUserManager extends AbstractUserManager<SpongeUser> implements LPSubjectCollection {
private final LPSpongePlugin plugin;
private SubjectCollection spongeProxy = null;
private final LoadingCache<UserIdentifier, SpongeUser> objects = Caffeine.newBuilder()
.build(this::apply);
private final LoadingCache<UUID, LPSubject> subjectLoadingCache = Caffeine.<UUID, LPSubject>newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build(u -> {
// clock in with the housekeeper
getHouseKeeper().registerUsage(u);
// check if the user instance is already loaded.
SpongeUser user = getIfLoaded(u);
if (user != null) {
@ -100,6 +97,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
});
public SpongeUserManager(LPSpongePlugin plugin) {
super(plugin, UserHousekeeper.timeoutSettings(10, TimeUnit.MINUTES));
this.plugin = plugin;
}
@ -110,103 +108,6 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
new SpongeUser(id.getUuid(), id.getUsername().get(), this.plugin);
}
/* ------------------------------------------
* Manager methods
* ------------------------------------------ */
@Override
public Map<UserIdentifier, SpongeUser> getAll() {
return ImmutableMap.copyOf(this.objects.asMap());
}
@Override
public SpongeUser getOrMake(UserIdentifier id) {
SpongeUser ret = this.objects.get(id);
if (id.getUsername().isPresent()) {
ret.setName(id.getUsername().get(), false);
}
return ret;
}
@Override
public SpongeUser getIfLoaded(UserIdentifier id) {
return this.objects.getIfPresent(id);
}
@Override
public boolean isLoaded(UserIdentifier id) {
return this.objects.asMap().containsKey(id);
}
@Override
public void unload(UserIdentifier id) {
if (id != null) {
this.objects.invalidate(id);
}
}
@Override
public void unload(User t) {
if (t != null) {
unload(t.getId());
}
}
@Override
public void unloadAll() {
this.objects.invalidateAll();
}
/* ------------------------------------------
* UserManager methods
* ------------------------------------------ */
@Override
public SpongeUser getByUsername(String name) {
for (SpongeUser user : getAll().values()) {
Optional<String> n = user.getName();
if (n.isPresent() && n.get().equalsIgnoreCase(name)) {
return user;
}
}
return null;
}
@Override
public SpongeUser getIfLoaded(UUID uuid) {
return getIfLoaded(UserIdentifier.of(uuid, null));
}
@Override
public boolean giveDefaultIfNeeded(User user, boolean save) {
return GenericUserManager.giveDefaultIfNeeded(user, save, this.plugin);
}
@Override
public boolean cleanup(User user) {
// Do nothing - this instance uses other means in order to cleanup
return false;
}
@Override
public void scheduleUnload(UUID uuid) {
// Do nothing - this instance uses other means in order to cleanup
}
@Override
public CompletableFuture<Void> updateAllUsers() {
return CompletableFuture.runAsync(
() -> this.plugin.getOnlinePlayers()
.map(u -> this.plugin.getUuidCache().getUUID(u))
.forEach(u -> this.plugin.getStorage().loadUser(u, null).join()),
this.plugin.getScheduler().async()
);
}
/* ------------------------------------------
* SubjectCollection methods
* ------------------------------------------ */
@Override
public synchronized SubjectCollection sponge() {
if (this.spongeProxy == null) {
@ -216,6 +117,10 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
return this.spongeProxy;
}
public LPSpongePlugin getPlugin() {
return this.plugin;
}
@Override
public LuckPermsService getService() {
return this.plugin.getService();
@ -358,7 +263,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
@Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) {
return this.objects.asMap().values().stream()
return getAll().values().stream()
.map(SpongeUser::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
@ -367,7 +272,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
@Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) {
return this.objects.asMap().values().stream()
return getAll().values().stream()
.map(SpongeUser::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
@ -379,8 +284,4 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
return getService().getDefaultSubjects().loadSubject(getIdentifier()).join();
}
public LPSpongePlugin getPlugin() {
return this.plugin;
}
}