diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/collections/GroupCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/collections/GroupCollection.java index c34160a51..6caea5bfb 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/collections/GroupCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/collections/GroupCollection.java @@ -23,9 +23,17 @@ package me.lucko.luckperms.sponge.service.collections; import co.aikar.timings.Timing; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.UncheckedExecutionException; import lombok.NonNull; import me.lucko.luckperms.api.context.ContextSet; +import me.lucko.luckperms.common.groups.Group; import me.lucko.luckperms.common.groups.GroupManager; +import me.lucko.luckperms.common.utils.ArgumentChecker; import me.lucko.luckperms.common.utils.ImmutableCollectors; import me.lucko.luckperms.sponge.service.LuckPermsGroupSubject; import me.lucko.luckperms.sponge.service.LuckPermsService; @@ -40,12 +48,31 @@ import org.spongepowered.api.util.Tristate; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; public class GroupCollection implements SubjectCollection { private final LuckPermsService service; private final GroupManager manager; private final SimpleCollection fallback; + private final LoadingCache groups = CacheBuilder.newBuilder() + .build(new CacheLoader() { + @Override + public LuckPermsGroupSubject load(String id) throws Exception { + Group group = manager.get(id); + if (group == null) { + throw new IllegalStateException("User not loaded"); + } + + return LuckPermsGroupSubject.wrapGroup(group, service); + } + + @Override + public ListenableFuture reload(String str, LuckPermsGroupSubject s) { + return Futures.immediateFuture(s); // Never needs to be refreshed. + } + }); + public GroupCollection(LuckPermsService service, GroupManager manager) { this.service = service; this.manager = manager; @@ -60,24 +87,39 @@ public class GroupCollection implements SubjectCollection { @Override public Subject get(@NonNull String id) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.GROUP_COLLECTION_GET)) { - if (manager.isLoaded(id)) { - return LuckPermsGroupSubject.wrapGroup(manager.get(id), service); + id = id.toLowerCase(); + if (ArgumentChecker.checkName(id)) { + service.getPlugin().getLog().warn("Couldn't get group subject for id: " + id + " (invalid name)"); + return fallback.get(id); // fallback to transient collection } - return fallback.get(id); + // check if the user is loaded in memory. hopefully this call is not on the main thread. :( + if (!manager.isLoaded(id)) { + service.getPlugin().getLog().warn("Group Subject '" + id + "' was requested, but is not loaded in memory. Loading it from storage now."); + long startTime = System.currentTimeMillis(); + service.getPlugin().getDatastore().createAndLoadGroup(id).getUnchecked(); + service.getPlugin().getLog().warn("Loading '" + id + "' took " + (System.currentTimeMillis() - startTime) + " ms."); + } + + try { + return groups.get(id); + } catch (ExecutionException | UncheckedExecutionException e) { + service.getPlugin().getLog().warn("Unable to get group subject '" + id + "' from memory."); + e.printStackTrace(); + return fallback.get(id); + } } } @Override public boolean hasRegistered(@NonNull String id) { - return manager.isLoaded(id); + id = id.toLowerCase(); + return !ArgumentChecker.checkName(id) && (groups.asMap().containsKey(id) || manager.isLoaded(id)); } @Override public Iterable getAllSubjects() { - return manager.getAll().values().stream() - .map(u -> LuckPermsGroupSubject.wrapGroup(u, service)) - .collect(ImmutableCollectors.toImmutableList()); + return groups.asMap().values().stream().collect(ImmutableCollectors.toImmutableList()); } @Override @@ -88,8 +130,7 @@ public class GroupCollection implements SubjectCollection { @Override public Map getAllWithPermission(@NonNull Set contexts, @NonNull String node) { ContextSet cs = LuckPermsService.convertContexts(contexts); - return manager.getAll().values().stream() - .map(u -> LuckPermsGroupSubject.wrapGroup(u, service)) + return groups.asMap().values().stream() .filter(sub -> sub.getPermissionValue(cs, node) != Tristate.UNDEFINED) .collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(cs, node).asBoolean())); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/collections/UserCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/collections/UserCollection.java index cbb3a73bf..ebe9a369d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/collections/UserCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/collections/UserCollection.java @@ -33,6 +33,7 @@ import lombok.NonNull; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.common.commands.utils.Util; import me.lucko.luckperms.common.users.User; +import me.lucko.luckperms.common.users.UserIdentifier; import me.lucko.luckperms.common.users.UserManager; import me.lucko.luckperms.common.utils.ImmutableCollectors; import me.lucko.luckperms.sponge.service.LuckPermsService; @@ -47,7 +48,6 @@ import org.spongepowered.api.service.permission.SubjectData; import org.spongepowered.api.util.Tristate; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -97,50 +97,41 @@ public class UserCollection implements SubjectCollection { @Override public Subject get(@NonNull String id) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.USER_COLLECTION_GET)) { - Optional s = getIfLoaded(id); - if (s.isPresent()) { - return s.get(); + UUID uuid = Util.parseUuid(id); + if (uuid == null) { + service.getPlugin().getLog().warn("Couldn't get user subject for id: " + id + " (not a uuid)"); + return fallback.get(id); // fallback to the transient collection } - // Fallback to the other collection. This Subject instance will never be persisted. - return fallback.get(id); - } - } + UUID u = service.getPlugin().getUuidCache().getUUID(uuid); - private Optional getIfLoaded(String id) { - UUID uuid = Util.parseUuid(id); - - find: - if (uuid == null) { - for (LuckPermsUserSubject subject : users.asMap().values()) { - if (subject.getUser().getName().equals(id)) { - return Optional.of(subject); - } + // check if the user is loaded in memory. hopefully this call is not on the main thread. :( + if (!manager.isLoaded(UserIdentifier.of(u, null))) { + service.getPlugin().getLog().warn("User Subject '" + u + "' was requested, but is not loaded in memory. Loading them from storage now."); + long startTime = System.currentTimeMillis(); + service.getPlugin().getDatastore().loadUser(u, "null").getUnchecked(); + service.getPlugin().getLog().warn("Loading '" + u + "' took " + (System.currentTimeMillis() - startTime) + " ms."); } - for (User user : manager.getAll().values()) { - if (user.getName().equalsIgnoreCase(id)) { - uuid = user.getUuid(); - break find; - } + try { + return users.get(u); + } catch (ExecutionException | UncheckedExecutionException e) { + service.getPlugin().getLog().warn("Unable to get user subject '" + u + "' from memory."); + e.printStackTrace(); + return fallback.get(u.toString()); } } - - if (uuid == null) { - return Optional.empty(); - } - - UUID internal = service.getPlugin().getUuidCache().getUUID(uuid); - try { - return Optional.of(users.get(internal)); - } catch (ExecutionException | UncheckedExecutionException e) { - return Optional.empty(); - } } @Override public boolean hasRegistered(@NonNull String id) { - return getIfLoaded(id).isPresent(); + UUID uuid = Util.parseUuid(id); + if (uuid == null) { + return false; + } + + UUID internal = service.getPlugin().getUuidCache().getUUID(uuid); + return users.asMap().containsKey(internal) || manager.isLoaded(UserIdentifier.of(internal, null)); } @Override