Group.getUsers() method
Dymeth opened this issue ยท 9 comments
Hello. I am making a plugin for BungeeCord, which needs to get a list of all players in a particular group (including offline players). I did not find this function in the API. A similar situation is described here: spigotmc.org/threads/417525
Earlier, to solve my problem, I use the implementation code of the command /lp group <group> listmembers
(copy-paste from me.lucko.luckperms.common.commands.group.GroupListMembers with some reflection).
This was quite inconvenient, and besides, after a recent update, my code stopped working, and I could not update it to a working state.
It would be great to be able to query the data of all the players in the group. Or, at least their UUIDs or even nicknames. I would be fine with any option that does not require using the LuckPerms core instead of LP API.
It could be Group.getUsers() or something like that
You can use the UserManager#searchAll(NodeMatcher)
to find all users having the InheritanceNode
representing the group. Check out the factory methods for NodeMatcher
to see how to create one and pass to the searchAll
method.
So this is my solution. Ugh, it took about an hour to figure it out. It should be keep in mind that this method should be called asynchronously, since it loads data directly from storage (disk, database, etc.).
However, I think it would be great to see a simpler solution to this question in the API - I'm not the only one who needs this feature.
private List<User> getUsersInGroup(String groupName) {
LuckPerms api = LuckPermsProvider.get();
Group group = api.getGroupManager().getGroup(groupName);
if (group == null) throw new IllegalArgumentException("Group " + groupName + " not found");
UserManager userManager = api.getUserManager();
List<User> users = new ArrayList<>();
for (UUID uuid : userManager.searchAll(NodeMatcher.key(InheritanceNode.builder(group).build())).join().keySet()) {
User user = userManager.isLoaded(uuid) ? userManager.getUser(uuid) : userManager.loadUser(uuid).join();
if (user == null) throw new IllegalStateException("Could not load data of " + uuid);
users.add(user);
}
return users;
}
this method should be called asynchronously, since it loads data directly from storage
So would a possible Group#getMembers()
method? Users aren't magically loaded into memory already.
You would still need to loop through all members to add the node you want etc.
So would a possible
Group#getMembers()
method? Users aren't magically loaded into memory already.You would still need to loop through all members to add the node you want etc.
This problem can be solved by returning CompletableFuture. Or by passing Callback/Consumer to the method. There are many options...
A bit more performant.. will load users in parallel.
I'm not sure whether this is something that I want to include as a utility method in the API itself - loading users is an expensive operation (relatively compared to the searchAll call) and so you should only do it if you really do need more than a UUID.
private CompletableFuture<List<User>> getUsersInGroup(String groupName) {
NodeMatcher<InheritanceNode> matcher = NodeMatcher.key(InheritanceNode.builder(groupName).build());
return luckPerms.getUserManager().searchAll(matcher).thenComposeAsync(results -> {
List<CompletableFuture<User>> users = new ArrayList<>();
return CompletableFuture.allOf(
results.keySet().stream()
.map(uuid -> luckPerms.getUserManager().loadUser(uuid))
.peek(users::add)
.toArray(CompletableFuture[]::new)
).thenApply(x -> users.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
});
}
A bit more performant.. will load users in parallel.
I'm not sure whether this is something that I want to include as a utility method in the API itself - loading users is an expensive operation (relatively compared to the searchAll call) and so you should only do it if you really do need more than a UUID.
Yes, I need exactly the User meta data.
But if we talk about the API, I think that getting the UUID will be enough. After receiving the UUID, everyone will be able to load user data if necessary. So the method I suggested could be called .getMemberIds()