Rewrite the way user instances are cleaned up and unloaded - towards #674
This commit is contained in:
@@ -52,8 +52,7 @@ import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.NoopLocaleManager;
|
||||
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
|
||||
import me.lucko.luckperms.common.logging.SenderLogger;
|
||||
import me.lucko.luckperms.common.managers.GenericTrackManager;
|
||||
import me.lucko.luckperms.common.managers.TrackManager;
|
||||
import me.lucko.luckperms.common.managers.track.StandardTrackManager;
|
||||
import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
@@ -168,7 +167,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
||||
private LuckPermsConfiguration configuration;
|
||||
private SpongeUserManager userManager;
|
||||
private SpongeGroupManager groupManager;
|
||||
private TrackManager trackManager;
|
||||
private StandardTrackManager trackManager;
|
||||
private Storage storage;
|
||||
private FileWatcher fileWatcher = null;
|
||||
private ExtendedMessagingService messagingService = null;
|
||||
@@ -243,7 +242,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
||||
this.uuidCache = new UuidCache(this);
|
||||
this.userManager = new SpongeUserManager(this);
|
||||
this.groupManager = new SpongeGroupManager(this);
|
||||
this.trackManager = new GenericTrackManager(this);
|
||||
this.trackManager = new StandardTrackManager(this);
|
||||
this.calculatorFactory = new SpongeCalculatorFactory(this);
|
||||
this.cachedStateManager = new CachedStateManager();
|
||||
|
||||
@@ -561,7 +560,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackManager getTrackManager() {
|
||||
public StandardTrackManager getTrackManager() {
|
||||
return this.trackManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
package me.lucko.luckperms.sponge;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
|
||||
import me.lucko.luckperms.common.plugin.SchedulerTask;
|
||||
import me.lucko.luckperms.common.utils.SafeIterator;
|
||||
|
||||
import org.spongepowered.api.scheduler.Scheduler;
|
||||
import org.spongepowered.api.scheduler.Task;
|
||||
|
||||
import java.util.Set;
|
||||
@@ -35,12 +38,16 @@ import java.util.concurrent.Executor;
|
||||
|
||||
public class SpongeSchedulerAdapter implements SchedulerAdapter {
|
||||
private final LPSpongePlugin plugin;
|
||||
private final Set<Task> tasks = ConcurrentHashMap.newKeySet();
|
||||
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public SpongeSchedulerAdapter(LPSpongePlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
private Scheduler scheduler() {
|
||||
return this.plugin.getSpongeScheduler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor async() {
|
||||
return this.plugin.getAsyncExecutorService();
|
||||
@@ -62,29 +69,72 @@ public class SpongeSchedulerAdapter implements SchedulerAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void asyncRepeating(Runnable runnable, long intervalTicks) {
|
||||
Task task = this.plugin.getSpongeScheduler().createTaskBuilder().async().intervalTicks(intervalTicks).delayTicks(intervalTicks).execute(runnable).submit(this.plugin);
|
||||
this.tasks.add(task);
|
||||
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
|
||||
Task task = scheduler().createTaskBuilder()
|
||||
.async()
|
||||
.intervalTicks(intervalTicks)
|
||||
.delayTicks(intervalTicks)
|
||||
.execute(runnable)
|
||||
.submit(this.plugin);
|
||||
|
||||
SchedulerTask wrapped = new SpongeSchedulerTask(task);
|
||||
this.tasks.add(wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncRepeating(Runnable runnable, long intervalTicks) {
|
||||
Task task = this.plugin.getSpongeScheduler().createTaskBuilder().intervalTicks(intervalTicks).delayTicks(intervalTicks).execute(runnable).submit(this.plugin);
|
||||
this.tasks.add(task);
|
||||
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
|
||||
Task task = scheduler().createTaskBuilder()
|
||||
.intervalTicks(intervalTicks)
|
||||
.delayTicks(intervalTicks)
|
||||
.execute(runnable)
|
||||
.submit(this.plugin);
|
||||
|
||||
SchedulerTask wrapped = new SpongeSchedulerTask(task);
|
||||
this.tasks.add(wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void asyncLater(Runnable runnable, long delayTicks) {
|
||||
this.plugin.getSpongeScheduler().createTaskBuilder().async().delayTicks(delayTicks).execute(runnable).submit(this.plugin);
|
||||
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
|
||||
Task task = scheduler().createTaskBuilder()
|
||||
.async()
|
||||
.delayTicks(delayTicks)
|
||||
.execute(runnable)
|
||||
.submit(this.plugin);
|
||||
|
||||
SchedulerTask wrapped = new SpongeSchedulerTask(task);
|
||||
this.tasks.add(wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncLater(Runnable runnable, long delayTicks) {
|
||||
this.plugin.getSpongeScheduler().createTaskBuilder().delayTicks(delayTicks).execute(runnable).submit(this.plugin);
|
||||
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
|
||||
Task task = scheduler().createTaskBuilder()
|
||||
.delayTicks(delayTicks)
|
||||
.execute(runnable)
|
||||
.submit(this.plugin);
|
||||
|
||||
SchedulerTask wrapped = new SpongeSchedulerTask(task);
|
||||
this.tasks.add(wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
this.tasks.forEach(Task::cancel);
|
||||
SafeIterator.iterate(this.tasks, SchedulerTask::cancel);
|
||||
}
|
||||
|
||||
private static final class SpongeSchedulerTask implements SchedulerTask {
|
||||
private final Task task;
|
||||
|
||||
private SpongeSchedulerTask(Task task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
this.task.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -28,7 +28,7 @@ package me.lucko.luckperms.sponge.listeners;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.utils.LoginHelper;
|
||||
import me.lucko.luckperms.common.utils.AbstractLoginListener;
|
||||
import me.lucko.luckperms.common.utils.UuidCache;
|
||||
import me.lucko.luckperms.sponge.LPSpongePlugin;
|
||||
|
||||
@@ -45,13 +45,14 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SpongeConnectionListener {
|
||||
public class SpongeConnectionListener extends AbstractLoginListener {
|
||||
private final LPSpongePlugin plugin;
|
||||
|
||||
private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Set<UUID> deniedLogin = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
public SpongeConnectionListener(LPSpongePlugin plugin) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ public class SpongeConnectionListener {
|
||||
- creating a user instance in the UserManager for this connection.
|
||||
- setting up cached data. */
|
||||
try {
|
||||
User user = LoginHelper.loadUser(this.plugin, p.getUniqueId(), username, false);
|
||||
User user = loadUser(p.getUniqueId(), username);
|
||||
this.plugin.getEventFactory().handleUserLoginProcess(p.getUniqueId(), username, user);
|
||||
} catch (Exception ex) {
|
||||
this.plugin.getLog().severe("Exception occured whilst loading data for " + p.getUniqueId() + " - " + p.getName());
|
||||
@@ -165,6 +166,10 @@ public class SpongeConnectionListener {
|
||||
|
||||
// Unload the user from memory when they disconnect
|
||||
cache.clearCache(e.getTargetEntity().getUniqueId());
|
||||
|
||||
// Register with the housekeeper, so the User's instance will stick
|
||||
// around for a bit after they disconnect
|
||||
this.plugin.getUserManager().getHouseKeeper().registerUsage(e.getTargetEntity().getUniqueId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,8 +36,7 @@ import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.common.managers.GroupManager;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.managers.group.AbstractGroupManager;
|
||||
import me.lucko.luckperms.common.storage.DataConstraints;
|
||||
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
||||
import me.lucko.luckperms.sponge.LPSpongePlugin;
|
||||
@@ -61,15 +60,10 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
|
||||
|
||||
public class SpongeGroupManager extends AbstractGroupManager<SpongeGroup> implements LPSubjectCollection {
|
||||
private final LPSpongePlugin plugin;
|
||||
|
||||
private SubjectCollection spongeProxy = null;
|
||||
|
||||
private final LoadingCache<String, SpongeGroup> objects = Caffeine.newBuilder()
|
||||
.build(this::apply);
|
||||
|
||||
private final LoadingCache<String, LPSubject> subjectLoadingCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build(s -> {
|
||||
@@ -104,53 +98,6 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
|
||||
return new SpongeGroup(name, this.plugin);
|
||||
}
|
||||
|
||||
/* ------------------------------------------
|
||||
* Manager methods
|
||||
* ------------------------------------------ */
|
||||
|
||||
@Override
|
||||
public Map<String, SpongeGroup> getAll() {
|
||||
return ImmutableMap.copyOf(this.objects.asMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpongeGroup getOrMake(String id) {
|
||||
return this.objects.get(id.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpongeGroup getIfLoaded(String id) {
|
||||
return this.objects.getIfPresent(id.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded(String id) {
|
||||
return this.objects.asMap().containsKey(id.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload(String id) {
|
||||
if (id != null) {
|
||||
this.objects.invalidate(id.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload(Group t) {
|
||||
if (t != null) {
|
||||
unload(t.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadAll() {
|
||||
this.objects.invalidateAll();
|
||||
}
|
||||
|
||||
/* ------------------------------------------
|
||||
* SubjectCollection methods
|
||||
* ------------------------------------------ */
|
||||
|
||||
@Override
|
||||
public synchronized SubjectCollection sponge() {
|
||||
if (this.spongeProxy == null) {
|
||||
@@ -160,6 +107,10 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
|
||||
return this.spongeProxy;
|
||||
}
|
||||
|
||||
public LPSpongePlugin getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuckPermsService getService() {
|
||||
return this.plugin.getService();
|
||||
@@ -261,7 +212,7 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
|
||||
|
||||
@Override
|
||||
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) {
|
||||
return this.objects.asMap().values().stream()
|
||||
return getAll().values().stream()
|
||||
.map(SpongeGroup::sponge)
|
||||
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission)))
|
||||
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
|
||||
@@ -270,7 +221,7 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
|
||||
|
||||
@Override
|
||||
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) {
|
||||
return this.objects.asMap().values().stream()
|
||||
return getAll().values().stream()
|
||||
.map(SpongeGroup::sponge)
|
||||
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission)))
|
||||
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
|
||||
@@ -282,33 +233,4 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
|
||||
return getService().getDefaultSubjects().loadSubject(getIdentifier()).join();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getByDisplayName(String name) {
|
||||
// try to get an exact match first
|
||||
Group g = getIfLoaded(name);
|
||||
if (g != null) {
|
||||
return g;
|
||||
}
|
||||
|
||||
// then try exact display name matches
|
||||
for (Group group : getAll().values()) {
|
||||
if (group.getDisplayName().isPresent() && group.getDisplayName().get().equals(name)) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
// then try case insensitive name matches
|
||||
for (Group group : getAll().values()) {
|
||||
if (group.getDisplayName().isPresent() && group.getDisplayName().get().equalsIgnoreCase(name)) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public LPSpongePlugin getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,9 +35,8 @@ import com.google.common.collect.Maps;
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.managers.GenericUserManager;
|
||||
import me.lucko.luckperms.common.managers.UserManager;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.managers.user.AbstractUserManager;
|
||||
import me.lucko.luckperms.common.managers.user.UserHousekeeper;
|
||||
import me.lucko.luckperms.common.references.UserIdentifier;
|
||||
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
||||
import me.lucko.luckperms.sponge.LPSpongePlugin;
|
||||
@@ -62,18 +61,16 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SpongeUserManager implements UserManager, LPSubjectCollection {
|
||||
|
||||
public class SpongeUserManager extends AbstractUserManager<SpongeUser> implements LPSubjectCollection {
|
||||
private final LPSpongePlugin plugin;
|
||||
|
||||
private SubjectCollection spongeProxy = null;
|
||||
|
||||
private final LoadingCache<UserIdentifier, SpongeUser> objects = Caffeine.newBuilder()
|
||||
.build(this::apply);
|
||||
|
||||
private final LoadingCache<UUID, LPSubject> subjectLoadingCache = Caffeine.<UUID, LPSubject>newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build(u -> {
|
||||
// clock in with the housekeeper
|
||||
getHouseKeeper().registerUsage(u);
|
||||
|
||||
// check if the user instance is already loaded.
|
||||
SpongeUser user = getIfLoaded(u);
|
||||
if (user != null) {
|
||||
@@ -100,6 +97,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
|
||||
});
|
||||
|
||||
public SpongeUserManager(LPSpongePlugin plugin) {
|
||||
super(plugin, UserHousekeeper.timeoutSettings(10, TimeUnit.MINUTES));
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@@ -110,103 +108,6 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
|
||||
new SpongeUser(id.getUuid(), id.getUsername().get(), this.plugin);
|
||||
}
|
||||
|
||||
/* ------------------------------------------
|
||||
* Manager methods
|
||||
* ------------------------------------------ */
|
||||
|
||||
@Override
|
||||
public Map<UserIdentifier, SpongeUser> getAll() {
|
||||
return ImmutableMap.copyOf(this.objects.asMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpongeUser getOrMake(UserIdentifier id) {
|
||||
SpongeUser ret = this.objects.get(id);
|
||||
if (id.getUsername().isPresent()) {
|
||||
ret.setName(id.getUsername().get(), false);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpongeUser getIfLoaded(UserIdentifier id) {
|
||||
return this.objects.getIfPresent(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded(UserIdentifier id) {
|
||||
return this.objects.asMap().containsKey(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload(UserIdentifier id) {
|
||||
if (id != null) {
|
||||
this.objects.invalidate(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload(User t) {
|
||||
if (t != null) {
|
||||
unload(t.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadAll() {
|
||||
this.objects.invalidateAll();
|
||||
}
|
||||
|
||||
/* ------------------------------------------
|
||||
* UserManager methods
|
||||
* ------------------------------------------ */
|
||||
|
||||
@Override
|
||||
public SpongeUser getByUsername(String name) {
|
||||
for (SpongeUser user : getAll().values()) {
|
||||
Optional<String> n = user.getName();
|
||||
if (n.isPresent() && n.get().equalsIgnoreCase(name)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpongeUser getIfLoaded(UUID uuid) {
|
||||
return getIfLoaded(UserIdentifier.of(uuid, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean giveDefaultIfNeeded(User user, boolean save) {
|
||||
return GenericUserManager.giveDefaultIfNeeded(user, save, this.plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cleanup(User user) {
|
||||
// Do nothing - this instance uses other means in order to cleanup
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleUnload(UUID uuid) {
|
||||
// Do nothing - this instance uses other means in order to cleanup
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> updateAllUsers() {
|
||||
return CompletableFuture.runAsync(
|
||||
() -> this.plugin.getOnlinePlayers()
|
||||
.map(u -> this.plugin.getUuidCache().getUUID(u))
|
||||
.forEach(u -> this.plugin.getStorage().loadUser(u, null).join()),
|
||||
this.plugin.getScheduler().async()
|
||||
);
|
||||
}
|
||||
|
||||
/* ------------------------------------------
|
||||
* SubjectCollection methods
|
||||
* ------------------------------------------ */
|
||||
|
||||
@Override
|
||||
public synchronized SubjectCollection sponge() {
|
||||
if (this.spongeProxy == null) {
|
||||
@@ -216,6 +117,10 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
|
||||
return this.spongeProxy;
|
||||
}
|
||||
|
||||
public LPSpongePlugin getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuckPermsService getService() {
|
||||
return this.plugin.getService();
|
||||
@@ -358,7 +263,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
|
||||
|
||||
@Override
|
||||
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) {
|
||||
return this.objects.asMap().values().stream()
|
||||
return getAll().values().stream()
|
||||
.map(SpongeUser::sponge)
|
||||
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission)))
|
||||
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
|
||||
@@ -367,7 +272,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
|
||||
|
||||
@Override
|
||||
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) {
|
||||
return this.objects.asMap().values().stream()
|
||||
return getAll().values().stream()
|
||||
.map(SpongeUser::sponge)
|
||||
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission)))
|
||||
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
|
||||
@@ -379,8 +284,4 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
|
||||
return getService().getDefaultSubjects().loadSubject(getIdentifier()).join();
|
||||
}
|
||||
|
||||
public LPSpongePlugin getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user