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 com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.luckperms.common.plugin.SchedulerAdapter; 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 org.bukkit.scheduler.BukkitTask;
import java.util.Set; import java.util.Set;
@ -51,7 +54,7 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
private boolean useFallback = true; private boolean useFallback = true;
private final Set<BukkitTask> tasks = ConcurrentHashMap.newKeySet(); private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
public BukkitSchedulerAdapter(LPBukkitPlugin plugin) { public BukkitSchedulerAdapter(LPBukkitPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
@ -62,6 +65,10 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
this.async = new AsyncExecutor(); this.async = new AsyncExecutor();
} }
private BukkitScheduler scheduler() {
return this.plugin.getServer().getScheduler();
}
@Override @Override
public void doAsync(Runnable runnable) { public void doAsync(Runnable runnable) {
async().execute(runnable); async().execute(runnable);
@ -73,30 +80,32 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
} }
@Override @Override
public void asyncRepeating(Runnable runnable, long intervalTicks) { public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
BukkitTask task = this.plugin.getServer().getScheduler().runTaskTimerAsynchronously(this.plugin, runnable, intervalTicks, intervalTicks); SchedulerTask task = new BukkitSchedulerTask(scheduler().runTaskTimerAsynchronously(this.plugin, runnable, intervalTicks, intervalTicks));
this.tasks.add(task); this.tasks.add(task);
return task;
} }
@Override @Override
public void syncRepeating(Runnable runnable, long intervalTicks) { public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
BukkitTask task = this.plugin.getServer().getScheduler().runTaskTimer(this.plugin, runnable, intervalTicks, intervalTicks); SchedulerTask task = new BukkitSchedulerTask(scheduler().runTaskTimer(this.plugin, runnable, intervalTicks, intervalTicks));
this.tasks.add(task); this.tasks.add(task);
return task;
} }
@Override @Override
public void asyncLater(Runnable runnable, long delayTicks) { public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
this.plugin.getServer().getScheduler().runTaskLaterAsynchronously(this.plugin, runnable, delayTicks); return new BukkitSchedulerTask(scheduler().runTaskLaterAsynchronously(this.plugin, runnable, delayTicks));
} }
@Override @Override
public void syncLater(Runnable runnable, long delayTicks) { public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
this.plugin.getServer().getScheduler().scheduleSyncDelayedTask(this.plugin, runnable, delayTicks); return new BukkitSchedulerTask(scheduler().runTaskLater(this.plugin, runnable, delayTicks));
} }
@Override @Override
public void shutdown() { public void shutdown() {
this.tasks.forEach(BukkitTask::cancel); SafeIterator.iterate(this.tasks, SchedulerTask::cancel);
// wait for executor // wait for executor
this.asyncFallback.shutdown(); 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.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.logging.Logger; import me.lucko.luckperms.common.logging.Logger;
import me.lucko.luckperms.common.logging.SenderLogger; import me.lucko.luckperms.common.logging.SenderLogger;
import me.lucko.luckperms.common.managers.GenericGroupManager; import me.lucko.luckperms.common.managers.group.StandardGroupManager;
import me.lucko.luckperms.common.managers.GenericTrackManager; import me.lucko.luckperms.common.managers.track.StandardTrackManager;
import me.lucko.luckperms.common.managers.GenericUserManager; import me.lucko.luckperms.common.managers.user.StandardUserManager;
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.messaging.ExtendedMessagingService; import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; 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.ExpireTemporaryTask;
import me.lucko.luckperms.common.tasks.UpdateTask; import me.lucko.luckperms.common.tasks.UpdateTask;
import me.lucko.luckperms.common.treeview.PermissionVault; 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.utils.UuidCache;
import me.lucko.luckperms.common.verbose.VerboseHandler; import me.lucko.luckperms.common.verbose.VerboseHandler;
@ -115,9 +111,9 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private BukkitCommandExecutor commandManager; private BukkitCommandExecutor commandManager;
private VaultHookManager vaultHookManager = null; private VaultHookManager vaultHookManager = null;
private LuckPermsConfiguration configuration; private LuckPermsConfiguration configuration;
private UserManager userManager; private StandardUserManager userManager;
private GroupManager groupManager; private StandardGroupManager groupManager;
private TrackManager trackManager; private StandardTrackManager trackManager;
private Storage storage; private Storage storage;
private FileWatcher fileWatcher = null; private FileWatcher fileWatcher = null;
private ExtendedMessagingService messagingService = null; private ExtendedMessagingService messagingService = null;
@ -205,7 +201,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
this.scheduler.syncLater(new BukkitProcessorsSetupTask(this), 1L); this.scheduler.syncLater(new BukkitProcessorsSetupTask(this), 1L);
// register events // 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); getServer().getPluginManager().registerEvents(new BukkitPlatformListener(this), this);
if (getConfiguration().get(ConfigKeys.WATCH_FILES)) { if (getConfiguration().get(ConfigKeys.WATCH_FILES)) {
@ -237,9 +234,9 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
// load internal managers // load internal managers
getLog().info("Loading internal permission managers..."); getLog().info("Loading internal permission managers...");
this.uuidCache = new UuidCache(this); this.uuidCache = new UuidCache(this);
this.userManager = new GenericUserManager(this); this.userManager = new StandardUserManager(this);
this.groupManager = new GenericGroupManager(this); this.groupManager = new StandardGroupManager(this);
this.trackManager = new GenericTrackManager(this); this.trackManager = new StandardTrackManager(this);
this.calculatorFactory = new BukkitCalculatorFactory(this); this.calculatorFactory = new BukkitCalculatorFactory(this);
this.cachedStateManager = new CachedStateManager(); this.cachedStateManager = new CachedStateManager();
@ -312,7 +309,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
for (Player player : getServer().getOnlinePlayers()) { for (Player player : getServer().getOnlinePlayers()) {
this.scheduler.doAsync(() -> { this.scheduler.doAsync(() -> {
try { try {
LoginHelper.loadUser(this, player.getUniqueId(), player.getName(), false); connectionListener.loadUser(player.getUniqueId(), player.getName());
User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId())); User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId()));
if (user != null) { if (user != null) {
this.scheduler.doSync(() -> { this.scheduler.doSync(() -> {
@ -584,17 +581,17 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
} }
@Override @Override
public UserManager getUserManager() { public StandardUserManager getUserManager() {
return this.userManager; return this.userManager;
} }
@Override @Override
public GroupManager getGroupManager() { public StandardGroupManager getGroupManager() {
return this.groupManager; return this.groupManager;
} }
@Override @Override
public TrackManager getTrackManager() { public StandardTrackManager getTrackManager() {
return this.trackManager; 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.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.LoginHelper; import me.lucko.luckperms.common.utils.AbstractLoginListener;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -47,13 +47,14 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class BukkitConnectionListener implements Listener { public class BukkitConnectionListener extends AbstractLoginListener implements Listener {
private final LPBukkitPlugin plugin; private final LPBukkitPlugin plugin;
private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>()); private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>());
private final Set<UUID> deniedLogin = Collections.synchronizedSet(new HashSet<>()); private final Set<UUID> deniedLogin = Collections.synchronizedSet(new HashSet<>());
public BukkitConnectionListener(LPBukkitPlugin plugin) { public BukkitConnectionListener(LPBukkitPlugin plugin) {
super(plugin);
this.plugin = plugin; this.plugin = plugin;
} }
@ -86,7 +87,7 @@ public class BukkitConnectionListener implements Listener {
- creating a user instance in the UserManager for this connection. - creating a user instance in the UserManager for this connection.
- setting up cached data. */ - setting up cached data. */
try { 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); this.plugin.getEventFactory().handleUserLoginProcess(e.getUniqueId(), e.getName(), user);
} catch (Exception ex) { } catch (Exception ex) {
this.plugin.getLog().severe("Exception occured whilst loading data for " + e.getUniqueId() + " - " + e.getName()); this.plugin.getLog().severe("Exception occured whilst loading data for " + e.getUniqueId() + " - " + e.getName());
@ -115,12 +116,6 @@ public class BukkitConnectionListener implements Listener {
return; 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) @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 // Login event was cancelled by another plugin since we first loaded their data
if (denied || e.getResult() != PlayerLoginEvent.Result.ALLOWED) { if (denied || e.getResult() != PlayerLoginEvent.Result.ALLOWED) {
// Schedule cleanup of this user.
this.plugin.getUserManager().scheduleUnload(e.getPlayer().getUniqueId());
return; return;
} }
@ -206,8 +199,9 @@ public class BukkitConnectionListener implements Listener {
player.setOp(false); player.setOp(false);
} }
// Request that the users data is unloaded. // Register with the housekeeper, so the User's instance will stick
this.plugin.getUserManager().scheduleUnload(player.getUniqueId()); // 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; package me.lucko.luckperms.bungee;
import me.lucko.luckperms.common.plugin.SchedulerAdapter; 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.ScheduledTask;
import net.md_5.bungee.api.scheduler.TaskScheduler;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -35,16 +38,32 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class BungeeSchedulerAdapter implements SchedulerAdapter { 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 LPBungeePlugin plugin;
private final Executor asyncExecutor; private final Executor asyncExecutor;
private final Set<ScheduledTask> tasks = ConcurrentHashMap.newKeySet(); private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
public BungeeSchedulerAdapter(LPBungeePlugin plugin) { public BungeeSchedulerAdapter(LPBungeePlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
this.asyncExecutor = r -> plugin.getProxy().getScheduler().runAsync(plugin, r); this.asyncExecutor = r -> plugin.getProxy().getScheduler().runAsync(plugin, r);
} }
private TaskScheduler scheduler() {
return this.plugin.getProxy().getScheduler();
}
@Override @Override
public Executor async() { public Executor async() {
return this.asyncExecutor; return this.asyncExecutor;
@ -66,30 +85,44 @@ public class BungeeSchedulerAdapter implements SchedulerAdapter {
} }
@Override @Override
public void asyncRepeating(Runnable runnable, long intervalTicks) { public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
long millis = intervalTicks * 50L; // convert from ticks to milliseconds long millis = ticksToMillis(intervalTicks);
ScheduledTask task = this.plugin.getProxy().getScheduler().schedule(this.plugin, runnable, millis, millis, TimeUnit.MILLISECONDS); SchedulerTask task = new BungeeSchedulerTask(scheduler().schedule(this.plugin, runnable, millis, millis, TimeUnit.MILLISECONDS));
this.tasks.add(task); this.tasks.add(task);
return task;
} }
@Override @Override
public void syncRepeating(Runnable runnable, long intervalTicks) { public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
asyncRepeating(runnable, intervalTicks); return asyncRepeating(runnable, intervalTicks);
} }
@Override @Override
public void asyncLater(Runnable runnable, long delayTicks) { public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
long millis = delayTicks * 50L; // convert from ticks to milliseconds return new BungeeSchedulerTask(scheduler().schedule(this.plugin, runnable, ticksToMillis(delayTicks), TimeUnit.MILLISECONDS));
this.plugin.getProxy().getScheduler().schedule(this.plugin, runnable, millis, TimeUnit.MILLISECONDS);
} }
@Override @Override
public void syncLater(Runnable runnable, long delayTicks) { public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
asyncLater(runnable, delayTicks); return asyncLater(runnable, delayTicks);
} }
@Override @Override
public void shutdown() { 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.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.logging.Logger; import me.lucko.luckperms.common.logging.Logger;
import me.lucko.luckperms.common.logging.SenderLogger; import me.lucko.luckperms.common.logging.SenderLogger;
import me.lucko.luckperms.common.managers.GenericGroupManager; import me.lucko.luckperms.common.managers.group.StandardGroupManager;
import me.lucko.luckperms.common.managers.GenericTrackManager; import me.lucko.luckperms.common.managers.track.StandardTrackManager;
import me.lucko.luckperms.common.managers.GenericUserManager; import me.lucko.luckperms.common.managers.user.StandardUserManager;
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.messaging.ExtendedMessagingService; import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@ -101,9 +98,9 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
private SchedulerAdapter scheduler; private SchedulerAdapter scheduler;
private CommandManager commandManager; private CommandManager commandManager;
private LuckPermsConfiguration configuration; private LuckPermsConfiguration configuration;
private UserManager userManager; private StandardUserManager userManager;
private GroupManager groupManager; private StandardGroupManager groupManager;
private TrackManager trackManager; private StandardTrackManager trackManager;
private Storage storage; private Storage storage;
private FileWatcher fileWatcher = null; private FileWatcher fileWatcher = null;
private ExtendedMessagingService messagingService = null; private ExtendedMessagingService messagingService = null;
@ -183,9 +180,9 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
// load internal managers // load internal managers
getLog().info("Loading internal permission managers..."); getLog().info("Loading internal permission managers...");
this.uuidCache = new UuidCache(this); this.uuidCache = new UuidCache(this);
this.userManager = new GenericUserManager(this); this.userManager = new StandardUserManager(this);
this.groupManager = new GenericGroupManager(this); this.groupManager = new StandardGroupManager(this);
this.trackManager = new GenericTrackManager(this); this.trackManager = new StandardTrackManager(this);
this.calculatorFactory = new BungeeCalculatorFactory(this); this.calculatorFactory = new BungeeCalculatorFactory(this);
this.cachedStateManager = new CachedStateManager(); this.cachedStateManager = new CachedStateManager();
@ -394,17 +391,17 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
} }
@Override @Override
public UserManager getUserManager() { public StandardUserManager getUserManager() {
return this.userManager; return this.userManager;
} }
@Override @Override
public GroupManager getGroupManager() { public StandardGroupManager getGroupManager() {
return this.groupManager; return this.groupManager;
} }
@Override @Override
public TrackManager getTrackManager() { public StandardTrackManager getTrackManager() {
return this.trackManager; 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.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.LoginHelper; import me.lucko.luckperms.common.utils.AbstractLoginListener;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.PendingConnection;
@ -43,10 +43,11 @@ import net.md_5.bungee.event.EventPriority;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class BungeeConnectionListener implements Listener { public class BungeeConnectionListener extends AbstractLoginListener implements Listener {
private final LPBungeePlugin plugin; private final LPBungeePlugin plugin;
public BungeeConnectionListener(LPBungeePlugin plugin) { public BungeeConnectionListener(LPBungeePlugin plugin) {
super(plugin);
this.plugin = plugin; this.plugin = plugin;
} }
@ -82,7 +83,7 @@ public class BungeeConnectionListener implements Listener {
- creating a user instance in the UserManager for this connection. - creating a user instance in the UserManager for this connection.
- setting up cached data. */ - setting up cached data. */
try { 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); this.plugin.getEventFactory().handleUserLoginProcess(c.getUniqueId(), c.getName(), user);
} catch (Exception ex) { } catch (Exception ex) {
this.plugin.getLog().severe("Exception occured whilst loading data for " + c.getUniqueId() + " - " + c.getName()); 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. // finally, complete our intent to modify state, so the proxy can continue handling the connection.
e.completeIntent(this.plugin); 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 // Wait until the last priority to unload, so plugins can still perform permission checks on this event
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerQuit(PlayerDisconnectEvent e) { public void onPlayerQuit(PlayerDisconnectEvent e) {
// Request that the users data is unloaded. // Register with the housekeeper, so the User's instance will stick
this.plugin.getUserManager().scheduleUnload(e.getPlayer().getUniqueId()); // 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.plugin = plugin;
this.platformInfo = new ApiPlatformInfo(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.groupManager = new ApiGroupManager(plugin.getGroupManager());
this.trackManager = new ApiTrackManager(plugin.getTrackManager()); this.trackManager = new ApiTrackManager(plugin.getTrackManager());
this.actionLogger = new ApiActionLogger(plugin); this.actionLogger = new ApiActionLogger(plugin);

View File

@ -35,9 +35,9 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class ApiGroupManager implements GroupManager { 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; this.handle = handle;
} }

View File

@ -35,9 +35,9 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class ApiTrackManager implements TrackManager { 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; 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.User;
import me.lucko.luckperms.api.manager.UserManager; import me.lucko.luckperms.api.manager.UserManager;
import me.lucko.luckperms.common.api.delegates.model.ApiUser; import me.lucko.luckperms.common.api.delegates.model.ApiUser;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.references.UserIdentifier; import me.lucko.luckperms.common.references.UserIdentifier;
import java.util.Objects; import java.util.Objects;
@ -39,11 +38,9 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class ApiUserManager implements UserManager { public class ApiUserManager implements UserManager {
private final LuckPermsPlugin plugin; private final me.lucko.luckperms.common.managers.user.UserManager<?> handle;
private final me.lucko.luckperms.common.managers.UserManager handle;
public ApiUserManager(LuckPermsPlugin plugin, me.lucko.luckperms.common.managers.UserManager handle) { public ApiUserManager(me.lucko.luckperms.common.managers.user.UserManager<?> handle) {
this.plugin = plugin;
this.handle = handle; this.handle = handle;
} }
@ -76,6 +73,6 @@ public class ApiUserManager implements UserManager {
@Override @Override
public void cleanupUser(@Nonnull User user) { public void cleanupUser(@Nonnull User user) {
Objects.requireNonNull(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 @Override
public CompletableFuture<Boolean> loadUser(@Nonnull UUID uuid, String username) { public CompletableFuture<Boolean> loadUser(@Nonnull UUID uuid, String username) {
Objects.requireNonNull(uuid, "uuid"); 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); 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 * An abstract manager class
* *
* @param <I> the class used to identify each object held in this manager * @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() private final LoadingCache<I, T> objects = Caffeine.newBuilder()
.build(new CacheLoader<I, T>() { .build(new CacheLoader<I, T>() {
@ -85,9 +86,9 @@ public abstract class AbstractManager<I, T extends Identifiable<I>> implements M
} }
@Override @Override
public void unload(T t) { public void unload(C object) {
if (t != null) { if (object != null) {
unload(t.getId()); unload(object.getId());
} }
} }

View File

@ -34,16 +34,17 @@ import java.util.function.Function;
* A class which manages instances of a class * A class which manages instances of a class
* *
* @param <I> the class used to identify each object held in this manager * @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. * Gets a map containing all cached instances held by this manager.
* *
* @return all instances held in 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 * 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 * 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 * Unloads all objects from the manager

View File

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

View File

@ -23,11 +23,12 @@
* SOFTWARE. * 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; 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 * 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 * @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 * @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. * 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.model.Track;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; 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; private final LuckPermsPlugin plugin;
public GenericTrackManager(LuckPermsPlugin plugin) { public StandardTrackManager(LuckPermsPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
} }
@ -39,9 +39,4 @@ public class GenericTrackManager extends AbstractManager<String, Track> implemen
public Track apply(String name) { public Track apply(String name) {
return new Track(name, this.plugin); return new Track(name, this.plugin);
} }
@Override
protected String sanitizeIdentifier(String s) {
return s.toLowerCase();
}
} }

View File

@ -23,10 +23,11 @@
* SOFTWARE. * 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; 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. * 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.Node;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.config.ConfigKeys; 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.model.User;
import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@ -37,17 +38,20 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; 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 LuckPermsPlugin plugin;
private final UserHousekeeper housekeeper;
public GenericUserManager(LuckPermsPlugin plugin) { public AbstractUserManager(LuckPermsPlugin plugin, UserHousekeeper.TimeoutSettings timeoutSettings) {
this.plugin = plugin; this.plugin = plugin;
this.housekeeper = new UserHousekeeper(plugin, this, timeoutSettings);
this.plugin.getScheduler().asyncRepeating(this.housekeeper, 200L); // every 10 seconds
} }
@Override @Override
public User getOrMake(UserIdentifier id) { public T getOrMake(UserIdentifier id) {
User ret = super.getOrMake(id); T ret = super.getOrMake(id);
if (id.getUsername().isPresent()) { if (id.getUsername().isPresent()) {
ret.setName(id.getUsername().get(), false); ret.setName(id.getUsername().get(), false);
} }
@ -55,15 +59,8 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
} }
@Override @Override
public User apply(UserIdentifier id) { public T getByUsername(String name) {
return !id.getUsername().isPresent() ? for (T user : getAll().values()) {
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()) {
Optional<String> n = user.getName(); Optional<String> n = user.getName();
if (n.isPresent() && n.get().equalsIgnoreCase(name)) { if (n.isPresent() && n.get().equalsIgnoreCase(name)) {
return user; return user;
@ -73,59 +70,16 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
} }
@Override @Override
public User getIfLoaded(UUID uuid) { public T getIfLoaded(UUID uuid) {
return getIfLoaded(UserIdentifier.of(uuid, null)); return getIfLoaded(UserIdentifier.of(uuid, null));
} }
@Override @Override
public boolean giveDefaultIfNeeded(User user, boolean save) { 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; boolean work = false;
// check that they are actually a member of their primary group, otherwise remove it // 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(); String pg = user.getPrimaryGroup().getValue();
boolean has = false; boolean has = false;
@ -174,19 +128,40 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
} }
if (work && save) { if (work && save) {
plugin.getStorage().saveUser(user); this.plugin.getStorage().saveUser(user);
} }
return work; 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. * Check whether the user's state indicates that they should be persisted to storage.
* *
* @param user the user to check * @param user the user to check
* @return true if the user should be saved * @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) { if (user.getEnduringNodes().size() != 1) {
return true; 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. * 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.model.User;
import me.lucko.luckperms.common.references.Identifiable;
import me.lucko.luckperms.common.references.UserIdentifier; import me.lucko.luckperms.common.references.UserIdentifier;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; 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 * Get a user object by name
@ -40,7 +40,7 @@ public interface UserManager extends Manager<UserIdentifier, User> {
* @param name The name to search by * @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 * @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 * Get a user object by uuid
@ -48,7 +48,7 @@ public interface UserManager extends Manager<UserIdentifier, User> {
* @param uuid The uuid to search by * @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 * @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. * 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); 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 * 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.event.EventFactory;
import me.lucko.luckperms.common.locale.LocaleManager; import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.logging.Logger; import me.lucko.luckperms.common.logging.Logger;
import me.lucko.luckperms.common.managers.GroupManager; import me.lucko.luckperms.common.managers.group.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager; import me.lucko.luckperms.common.managers.track.TrackManager;
import me.lucko.luckperms.common.managers.UserManager; import me.lucko.luckperms.common.managers.user.UserManager;
import me.lucko.luckperms.common.messaging.ExtendedMessagingService; import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.storage.Storage; import me.lucko.luckperms.common.storage.Storage;
@ -75,21 +75,21 @@ public interface LuckPermsPlugin {
* *
* @return the user manager * @return the user manager
*/ */
UserManager getUserManager(); UserManager<?> getUserManager();
/** /**
* Gets the group manager instance for the platform * Gets the group manager instance for the platform
* *
* @return the group manager * @return the group manager
*/ */
GroupManager getGroupManager(); GroupManager<?> getGroupManager();
/** /**
* Gets the track manager instance for the platform * Gets the track manager instance for the platform
* *
* @return the track manager * @return the track manager
*/ */
TrackManager getTrackManager(); TrackManager<?> getTrackManager();
/** /**
* Gets the plugin's configuration * Gets the plugin's configuration

View File

@ -65,28 +65,28 @@ public interface SchedulerAdapter {
* @param runnable the runnable * @param runnable the runnable
* @param intervalTicks the interval in ticks. * @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. * 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 runnable the runnable
* @param intervalTicks the interval in ticks. * @param intervalTicks the interval in ticks.
*/ */
void syncRepeating(Runnable runnable, long intervalTicks); SchedulerTask syncRepeating(Runnable runnable, long intervalTicks);
/** /**
* Runs a runnable with a delay * Runs a runnable with a delay
* @param runnable the runnable * @param runnable the runnable
* @param delayTicks the delay in ticks * @param delayTicks the delay in ticks
*/ */
void asyncLater(Runnable runnable, long delayTicks); SchedulerTask asyncLater(Runnable runnable, long delayTicks);
/** /**
* Runs a runnable with a delay * Runs a runnable with a delay
* @param runnable the runnable * @param runnable the runnable
* @param delayTicks the delay in ticks * @param delayTicks the delay in ticks
*/ */
void syncLater(Runnable runnable, long delayTicks); SchedulerTask syncLater(Runnable runnable, long delayTicks);
/** /**
* Shuts down this executor instance * 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.CommandManager;
import me.lucko.luckperms.common.commands.utils.CommandUtils; import me.lucko.luckperms.common.commands.utils.CommandUtils;
import me.lucko.luckperms.common.contexts.ContextSetConfigurateSerializer; import me.lucko.luckperms.common.contexts.ContextSetConfigurateSerializer;
import me.lucko.luckperms.common.managers.GenericUserManager; import me.lucko.luckperms.common.managers.group.GroupManager;
import me.lucko.luckperms.common.managers.GroupManager; import me.lucko.luckperms.common.managers.track.TrackManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
@ -400,7 +399,7 @@ public abstract class ConfigurateDao extends AbstractDao {
saveUser(user); saveUser(user);
} }
} else { } else {
if (GenericUserManager.shouldSave(user)) { if (this.plugin.getUserManager().shouldSave(user)) {
user.clearNodes(); user.clearNodes();
user.getPrimaryGroup().setStoredValue(null); user.getPrimaryGroup().setStoredValue(null);
this.plugin.getUserManager().giveDefaultIfNeeded(user, false); this.plugin.getUserManager().giveDefaultIfNeeded(user, false);
@ -419,7 +418,7 @@ public abstract class ConfigurateDao extends AbstractDao {
public void saveUser(User user) throws Exception { public void saveUser(User user) throws Exception {
user.getIoLock().lock(); user.getIoLock().lock();
try { try {
if (!GenericUserManager.shouldSave(user)) { if (!this.plugin.getUserManager().shouldSave(user)) {
saveFile(StorageLocation.USER, user.getUuid().toString(), null); saveFile(StorageLocation.USER, user.getUuid().toString(), null);
} else { } else {
ConfigurationNode data = SimpleConfigurationNode.root(); ConfigurationNode data = SimpleConfigurationNode.root();
@ -562,7 +561,7 @@ public abstract class ConfigurateDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a group"); throw new RuntimeException("Exception occurred whilst loading a group");
} }
GroupManager gm = this.plugin.getGroupManager(); GroupManager<?> gm = this.plugin.getGroupManager();
gm.getAll().values().stream() gm.getAll().values().stream()
.filter(g -> !groups.contains(g.getName())) .filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload); .forEach(gm::unload);
@ -718,7 +717,7 @@ public abstract class ConfigurateDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a track"); throw new RuntimeException("Exception occurred whilst loading a track");
} }
TrackManager tm = this.plugin.getTrackManager(); TrackManager<?> tm = this.plugin.getTrackManager();
tm.getAll().values().stream() tm.getAll().values().stream()
.filter(t -> !tracks.contains(t.getName())) .filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload); .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.ExtendedLogEntry;
import me.lucko.luckperms.common.actionlog.Log; import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate; import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.managers.GenericUserManager; import me.lucko.luckperms.common.managers.group.GroupManager;
import me.lucko.luckperms.common.managers.GroupManager; import me.lucko.luckperms.common.managers.track.TrackManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User; 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)); c.replaceOne(new Document("_id", user.getUuid()), userToDoc(user));
} }
} else { } else {
if (GenericUserManager.shouldSave(user)) { if (this.plugin.getUserManager().shouldSave(user)) {
user.clearNodes(); user.clearNodes();
user.getPrimaryGroup().setStoredValue(null); user.getPrimaryGroup().setStoredValue(null);
this.plugin.getUserManager().giveDefaultIfNeeded(user, false); this.plugin.getUserManager().giveDefaultIfNeeded(user, false);
@ -287,7 +286,7 @@ public class MongoDao extends AbstractDao {
user.getIoLock().lock(); user.getIoLock().lock();
try { try {
MongoCollection<Document> c = this.database.getCollection(this.prefix + "users"); 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())); c.deleteOne(new Document("_id", user.getUuid()));
} else { } else {
c.replaceOne(new Document("_id", user.getUuid()), userToDoc(user), new UpdateOptions().upsert(true)); 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"); throw new RuntimeException("Exception occurred whilst loading a group");
} }
GroupManager gm = this.plugin.getGroupManager(); GroupManager<?> gm = this.plugin.getGroupManager();
gm.getAll().values().stream() gm.getAll().values().stream()
.filter(g -> !groups.contains(g.getName())) .filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload); .forEach(gm::unload);
@ -535,7 +534,7 @@ public class MongoDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a track"); throw new RuntimeException("Exception occurred whilst loading a track");
} }
TrackManager tm = this.plugin.getTrackManager(); TrackManager<?> tm = this.plugin.getTrackManager();
tm.getAll().values().stream() tm.getAll().values().stream()
.filter(t -> !tracks.contains(t.getName())) .filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload); .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.actionlog.Log;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate; import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.contexts.ContextSetJsonSerializer; import me.lucko.luckperms.common.contexts.ContextSetJsonSerializer;
import me.lucko.luckperms.common.managers.GenericUserManager; import me.lucko.luckperms.common.managers.group.GroupManager;
import me.lucko.luckperms.common.managers.GroupManager; import me.lucko.luckperms.common.managers.track.TrackManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
@ -364,7 +363,7 @@ public class SqlDao extends AbstractDao {
} else { } else {
// User has no data in storage. // User has no data in storage.
if (GenericUserManager.shouldSave(user)) { if (this.plugin.getUserManager().shouldSave(user)) {
user.clearNodes(); user.clearNodes();
user.getPrimaryGroup().setStoredValue(null); user.getPrimaryGroup().setStoredValue(null);
this.plugin.getUserManager().giveDefaultIfNeeded(user, false); this.plugin.getUserManager().giveDefaultIfNeeded(user, false);
@ -382,7 +381,7 @@ public class SqlDao extends AbstractDao {
user.getIoLock().lock(); user.getIoLock().lock();
try { try {
// Empty data - just delete from the DB. // Empty data - just delete from the DB.
if (!GenericUserManager.shouldSave(user)) { if (!this.plugin.getUserManager().shouldSave(user)) {
try (Connection c = this.provider.getConnection()) { try (Connection c = this.provider.getConnection()) {
try (PreparedStatement ps = c.prepareStatement(this.prefix.apply(USER_PERMISSIONS_DELETE))) { try (PreparedStatement ps = c.prepareStatement(this.prefix.apply(USER_PERMISSIONS_DELETE))) {
ps.setString(1, user.getUuid().toString()); ps.setString(1, user.getUuid().toString());
@ -643,7 +642,7 @@ public class SqlDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a group"); throw new RuntimeException("Exception occurred whilst loading a group");
} }
GroupManager gm = this.plugin.getGroupManager(); GroupManager<?> gm = this.plugin.getGroupManager();
gm.getAll().values().stream() gm.getAll().values().stream()
.filter(g -> !groups.contains(g.getName())) .filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload); .forEach(gm::unload);
@ -880,7 +879,7 @@ public class SqlDao extends AbstractDao {
throw new RuntimeException("Exception occurred whilst loading a track"); throw new RuntimeException("Exception occurred whilst loading a track");
} }
TrackManager tm = this.plugin.getTrackManager(); TrackManager<?> tm = this.plugin.getTrackManager();
tm.getAll().values().stream() tm.getAll().values().stream()
.filter(t -> !tracks.contains(t.getName())) .filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload); .forEach(tm::unload);

View File

@ -25,6 +25,7 @@
package me.lucko.luckperms.common.utils; 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.assignments.AssignmentRule;
import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
@ -34,47 +35,61 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture; 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 long startTime = System.currentTimeMillis();
final UuidCache cache = plugin.getUuidCache(); // register with the housekeeper to avoid accidental unloads
if (!plugin.getConfiguration().get(ConfigKeys.USE_SERVER_UUIDS)) { this.plugin.getUserManager().getHouseKeeper().registerUsage(u);
UUID uuid = plugin.getStorage().noBuffer().getUUID(username).join();
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) { if (uuid != null) {
cache.addToCache(u, uuid); cache.addToCache(u, uuid);
} else { } else {
// No previous data for this player // No previous data for this player
plugin.getEventFactory().handleUserFirstLogin(u, username); this.plugin.getEventFactory().handleUserFirstLogin(u, username);
cache.addToCache(u, u); cache.addToCache(u, u);
CompletableFuture<Void> future = plugin.getStorage().noBuffer().saveUUIDData(u, username); CompletableFuture<Void> future = this.plugin.getStorage().noBuffer().saveUUIDData(u, username);
if (joinUuidSave) { if (this.joinUuidSave) {
future.join(); future.join();
} }
} }
} else { } else {
String name = plugin.getStorage().noBuffer().getName(u).join(); String name = this.plugin.getStorage().noBuffer().getName(u).join();
if (name == null) { 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. // Online mode, no cache needed. This is just for name -> uuid lookup.
CompletableFuture<Void> future = plugin.getStorage().noBuffer().saveUUIDData(u, username); CompletableFuture<Void> future = this.plugin.getStorage().noBuffer().saveUUIDData(u, username);
if (joinUuidSave) { if (this.joinUuidSave) {
future.join(); 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) { if (user == null) {
throw new NullPointerException("User is null"); throw new NullPointerException("User is null");
} else { } else {
// Setup defaults for the user // Setup defaults for the user
boolean save = false; 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)) { if (rule.apply(user)) {
save = true; 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 they were given a default, persist the new assignments back to the storage.
if (save) { 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. // 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; final long time = System.currentTimeMillis() - startTime;
if (time >= 1000) { 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; 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.NoopLocaleManager;
import me.lucko.luckperms.common.locale.SimpleLocaleManager; import me.lucko.luckperms.common.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.logging.SenderLogger; import me.lucko.luckperms.common.logging.SenderLogger;
import me.lucko.luckperms.common.managers.GenericTrackManager; import me.lucko.luckperms.common.managers.track.StandardTrackManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.messaging.ExtendedMessagingService; import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@ -168,7 +167,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
private LuckPermsConfiguration configuration; private LuckPermsConfiguration configuration;
private SpongeUserManager userManager; private SpongeUserManager userManager;
private SpongeGroupManager groupManager; private SpongeGroupManager groupManager;
private TrackManager trackManager; private StandardTrackManager trackManager;
private Storage storage; private Storage storage;
private FileWatcher fileWatcher = null; private FileWatcher fileWatcher = null;
private ExtendedMessagingService messagingService = null; private ExtendedMessagingService messagingService = null;
@ -243,7 +242,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
this.uuidCache = new UuidCache(this); this.uuidCache = new UuidCache(this);
this.userManager = new SpongeUserManager(this); this.userManager = new SpongeUserManager(this);
this.groupManager = new SpongeGroupManager(this); this.groupManager = new SpongeGroupManager(this);
this.trackManager = new GenericTrackManager(this); this.trackManager = new StandardTrackManager(this);
this.calculatorFactory = new SpongeCalculatorFactory(this); this.calculatorFactory = new SpongeCalculatorFactory(this);
this.cachedStateManager = new CachedStateManager(); this.cachedStateManager = new CachedStateManager();
@ -561,7 +560,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
} }
@Override @Override
public TrackManager getTrackManager() { public StandardTrackManager getTrackManager() {
return this.trackManager; return this.trackManager;
} }

View File

@ -26,7 +26,10 @@
package me.lucko.luckperms.sponge; package me.lucko.luckperms.sponge;
import me.lucko.luckperms.common.plugin.SchedulerAdapter; 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 org.spongepowered.api.scheduler.Task;
import java.util.Set; import java.util.Set;
@ -35,12 +38,16 @@ import java.util.concurrent.Executor;
public class SpongeSchedulerAdapter implements SchedulerAdapter { public class SpongeSchedulerAdapter implements SchedulerAdapter {
private final LPSpongePlugin plugin; private final LPSpongePlugin plugin;
private final Set<Task> tasks = ConcurrentHashMap.newKeySet(); private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
public SpongeSchedulerAdapter(LPSpongePlugin plugin) { public SpongeSchedulerAdapter(LPSpongePlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
} }
private Scheduler scheduler() {
return this.plugin.getSpongeScheduler();
}
@Override @Override
public Executor async() { public Executor async() {
return this.plugin.getAsyncExecutorService(); return this.plugin.getAsyncExecutorService();
@ -62,29 +69,72 @@ public class SpongeSchedulerAdapter implements SchedulerAdapter {
} }
@Override @Override
public void asyncRepeating(Runnable runnable, long intervalTicks) { public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
Task task = this.plugin.getSpongeScheduler().createTaskBuilder().async().intervalTicks(intervalTicks).delayTicks(intervalTicks).execute(runnable).submit(this.plugin); Task task = scheduler().createTaskBuilder()
this.tasks.add(task); .async()
.intervalTicks(intervalTicks)
.delayTicks(intervalTicks)
.execute(runnable)
.submit(this.plugin);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
} }
@Override @Override
public void syncRepeating(Runnable runnable, long intervalTicks) { public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
Task task = this.plugin.getSpongeScheduler().createTaskBuilder().intervalTicks(intervalTicks).delayTicks(intervalTicks).execute(runnable).submit(this.plugin); Task task = scheduler().createTaskBuilder()
this.tasks.add(task); .intervalTicks(intervalTicks)
.delayTicks(intervalTicks)
.execute(runnable)
.submit(this.plugin);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
} }
@Override @Override
public void asyncLater(Runnable runnable, long delayTicks) { public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
this.plugin.getSpongeScheduler().createTaskBuilder().async().delayTicks(delayTicks).execute(runnable).submit(this.plugin); Task task = scheduler().createTaskBuilder()
.async()
.delayTicks(delayTicks)
.execute(runnable)
.submit(this.plugin);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
} }
@Override @Override
public void syncLater(Runnable runnable, long delayTicks) { public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
this.plugin.getSpongeScheduler().createTaskBuilder().delayTicks(delayTicks).execute(runnable).submit(this.plugin); Task task = scheduler().createTaskBuilder()
.delayTicks(delayTicks)
.execute(runnable)
.submit(this.plugin);
SchedulerTask wrapped = new SpongeSchedulerTask(task);
this.tasks.add(wrapped);
return wrapped;
} }
@Override @Override
public void shutdown() { 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.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.LoginHelper; import me.lucko.luckperms.common.utils.AbstractLoginListener;
import me.lucko.luckperms.common.utils.UuidCache; import me.lucko.luckperms.common.utils.UuidCache;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
@ -45,13 +45,14 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class SpongeConnectionListener { public class SpongeConnectionListener extends AbstractLoginListener {
private final LPSpongePlugin plugin; private final LPSpongePlugin plugin;
private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>()); private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>());
private final Set<UUID> deniedLogin = Collections.synchronizedSet(new HashSet<>()); private final Set<UUID> deniedLogin = Collections.synchronizedSet(new HashSet<>());
public SpongeConnectionListener(LPSpongePlugin plugin) { public SpongeConnectionListener(LPSpongePlugin plugin) {
super(plugin);
this.plugin = plugin; this.plugin = plugin;
} }
@ -80,7 +81,7 @@ public class SpongeConnectionListener {
- creating a user instance in the UserManager for this connection. - creating a user instance in the UserManager for this connection.
- setting up cached data. */ - setting up cached data. */
try { 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); this.plugin.getEventFactory().handleUserLoginProcess(p.getUniqueId(), username, user);
} catch (Exception ex) { } catch (Exception ex) {
this.plugin.getLog().severe("Exception occured whilst loading data for " + p.getUniqueId() + " - " + p.getName()); 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 // Unload the user from memory when they disconnect
cache.clearCache(e.getTargetEntity().getUniqueId()); 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.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.event.cause.CreationCause; import me.lucko.luckperms.api.event.cause.CreationCause;
import me.lucko.luckperms.common.managers.GroupManager; import me.lucko.luckperms.common.managers.group.AbstractGroupManager;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.storage.DataConstraints; import me.lucko.luckperms.common.storage.DataConstraints;
import me.lucko.luckperms.common.utils.ImmutableCollectors; import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
@ -61,15 +60,10 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Predicate; import java.util.function.Predicate;
public class SpongeGroupManager implements GroupManager, LPSubjectCollection { public class SpongeGroupManager extends AbstractGroupManager<SpongeGroup> implements LPSubjectCollection {
private final LPSpongePlugin plugin; private final LPSpongePlugin plugin;
private SubjectCollection spongeProxy = null; private SubjectCollection spongeProxy = null;
private final LoadingCache<String, SpongeGroup> objects = Caffeine.newBuilder()
.build(this::apply);
private final LoadingCache<String, LPSubject> subjectLoadingCache = Caffeine.newBuilder() private final LoadingCache<String, LPSubject> subjectLoadingCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES) .expireAfterWrite(1, TimeUnit.MINUTES)
.build(s -> { .build(s -> {
@ -104,53 +98,6 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
return new SpongeGroup(name, this.plugin); 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 @Override
public synchronized SubjectCollection sponge() { public synchronized SubjectCollection sponge() {
if (this.spongeProxy == null) { if (this.spongeProxy == null) {
@ -160,6 +107,10 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
return this.spongeProxy; return this.spongeProxy;
} }
public LPSpongePlugin getPlugin() {
return this.plugin;
}
@Override @Override
public LuckPermsService getService() { public LuckPermsService getService() {
return this.plugin.getService(); return this.plugin.getService();
@ -261,7 +212,7 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
@Override @Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) { public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) {
return this.objects.asMap().values().stream() return getAll().values().stream()
.map(SpongeGroup::sponge) .map(SpongeGroup::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission))) .map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED) .filter(pair -> pair.getValue() != Tristate.UNDEFINED)
@ -270,7 +221,7 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
@Override @Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) { public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) {
return this.objects.asMap().values().stream() return getAll().values().stream()
.map(SpongeGroup::sponge) .map(SpongeGroup::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission))) .map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED) .filter(pair -> pair.getValue() != Tristate.UNDEFINED)
@ -282,33 +233,4 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
return getService().getDefaultSubjects().loadSubject(getIdentifier()).join(); 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.HeldPermission;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.managers.GenericUserManager; import me.lucko.luckperms.common.managers.user.AbstractUserManager;
import me.lucko.luckperms.common.managers.UserManager; import me.lucko.luckperms.common.managers.user.UserHousekeeper;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.references.UserIdentifier; import me.lucko.luckperms.common.references.UserIdentifier;
import me.lucko.luckperms.common.utils.ImmutableCollectors; import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
@ -62,18 +61,16 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Predicate; import java.util.function.Predicate;
public class SpongeUserManager implements UserManager, LPSubjectCollection { public class SpongeUserManager extends AbstractUserManager<SpongeUser> implements LPSubjectCollection {
private final LPSpongePlugin plugin; private final LPSpongePlugin plugin;
private SubjectCollection spongeProxy = null; 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() private final LoadingCache<UUID, LPSubject> subjectLoadingCache = Caffeine.<UUID, LPSubject>newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES) .expireAfterWrite(1, TimeUnit.MINUTES)
.build(u -> { .build(u -> {
// clock in with the housekeeper
getHouseKeeper().registerUsage(u);
// check if the user instance is already loaded. // check if the user instance is already loaded.
SpongeUser user = getIfLoaded(u); SpongeUser user = getIfLoaded(u);
if (user != null) { if (user != null) {
@ -100,6 +97,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
}); });
public SpongeUserManager(LPSpongePlugin plugin) { public SpongeUserManager(LPSpongePlugin plugin) {
super(plugin, UserHousekeeper.timeoutSettings(10, TimeUnit.MINUTES));
this.plugin = plugin; this.plugin = plugin;
} }
@ -110,103 +108,6 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
new SpongeUser(id.getUuid(), id.getUsername().get(), this.plugin); 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 @Override
public synchronized SubjectCollection sponge() { public synchronized SubjectCollection sponge() {
if (this.spongeProxy == null) { if (this.spongeProxy == null) {
@ -216,6 +117,10 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
return this.spongeProxy; return this.spongeProxy;
} }
public LPSpongePlugin getPlugin() {
return this.plugin;
}
@Override @Override
public LuckPermsService getService() { public LuckPermsService getService() {
return this.plugin.getService(); return this.plugin.getService();
@ -358,7 +263,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
@Override @Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) { public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) {
return this.objects.asMap().values().stream() return getAll().values().stream()
.map(SpongeUser::sponge) .map(SpongeUser::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission))) .map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED) .filter(pair -> pair.getValue() != Tristate.UNDEFINED)
@ -367,7 +272,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
@Override @Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) { public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) {
return this.objects.asMap().values().stream() return getAll().values().stream()
.map(SpongeUser::sponge) .map(SpongeUser::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission))) .map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED) .filter(pair -> pair.getValue() != Tristate.UNDEFINED)
@ -379,8 +284,4 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
return getService().getDefaultSubjects().loadSubject(getIdentifier()).join(); return getService().getDefaultSubjects().loadSubject(getIdentifier()).join();
} }
public LPSpongePlugin getPlugin() {
return this.plugin;
}
} }