Refactor Sponge subject handling

This commit is contained in:
Luck
2016-11-25 20:59:28 +00:00
Unverified
parent 443906da55
commit 18dd59ed0f
50 changed files with 919 additions and 741 deletions
@@ -41,12 +41,8 @@ import me.lucko.luckperms.common.contexts.ServerCalculator;
import me.lucko.luckperms.common.core.UuidCache;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.data.Importer;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.managers.impl.GenericGroupManager;
import me.lucko.luckperms.common.managers.impl.GenericTrackManager;
import me.lucko.luckperms.common.managers.impl.GenericUserManager;
import me.lucko.luckperms.common.messaging.RedisMessaging;
import me.lucko.luckperms.common.storage.Storage;
import me.lucko.luckperms.common.storage.StorageFactory;
@@ -55,6 +51,8 @@ import me.lucko.luckperms.common.tasks.UpdateTask;
import me.lucko.luckperms.common.utils.*;
import me.lucko.luckperms.sponge.commands.SpongeMainCommand;
import me.lucko.luckperms.sponge.contexts.WorldCalculator;
import me.lucko.luckperms.sponge.managers.SpongeGroupManager;
import me.lucko.luckperms.sponge.managers.SpongeUserManager;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.timings.LPTimings;
import me.lucko.luckperms.sponge.utils.VersionData;
@@ -119,8 +117,8 @@ public class LPSpongePlugin implements LuckPermsPlugin {
private final Set<UUID> ignoringLogs = ConcurrentHashMap.newKeySet();
private LPConfiguration configuration;
private UserManager userManager;
private GroupManager groupManager;
private SpongeUserManager userManager;
private SpongeGroupManager groupManager;
private TrackManager trackManager;
private Storage storage;
private RedisMessaging redisMessaging = null;
@@ -199,8 +197,8 @@ public class LPSpongePlugin implements LuckPermsPlugin {
// load internal managers
getLog().info("Loading internal permission managers...");
uuidCache = new UuidCache(getConfiguration().isOnlineMode());
userManager = new GenericUserManager(this);
groupManager = new GenericGroupManager(this);
userManager = new SpongeUserManager(this);
groupManager = new SpongeGroupManager(this);
trackManager = new GenericTrackManager();
importer = new Importer(commandManager);
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
@@ -110,7 +110,6 @@ public class SpongeListener extends AbstractListener {
public void onClientLeave(ClientConnectionEvent.Disconnect e) {
try (Timing ignored = plugin.getTimings().time(LPTiming.ON_CLIENT_LEAVE)) {
onLeave(e.getTargetEntity().getUniqueId());
plugin.getService().getUserSubjects().unload(plugin.getUuidCache().getUUID(e.getTargetEntity().getUniqueId()));
}
}
}
@@ -20,22 +20,23 @@
* SOFTWARE.
*/
package me.lucko.luckperms.sponge.service.collections;
package me.lucko.luckperms.sponge.managers;
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.collect.ImmutableMap;
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.core.model.Group;
import me.lucko.luckperms.common.managers.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.LPSpongePlugin;
import me.lucko.luckperms.sponge.model.SpongeGroup;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.simple.SimpleCollection;
import me.lucko.luckperms.sponge.timings.LPTiming;
@@ -48,37 +49,74 @@ 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;
public class SpongeGroupManager implements GroupManager, SubjectCollection {
private final LPSpongePlugin plugin;
private final SimpleCollection fallback;
private final LoadingCache<String, LuckPermsGroupSubject> groups = CacheBuilder.newBuilder()
.build(new CacheLoader<String, LuckPermsGroupSubject>() {
private final LoadingCache<String, SpongeGroup> objects = CacheBuilder.newBuilder()
.build(new CacheLoader<String, SpongeGroup>() {
@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);
public SpongeGroup load(String i) {
return apply(i);
}
@Override
public ListenableFuture<LuckPermsGroupSubject> reload(String str, LuckPermsGroupSubject s) {
return Futures.immediateFuture(s); // Never needs to be refreshed.
public ListenableFuture<SpongeGroup> reload(String i, SpongeGroup t) {
return Futures.immediateFuture(t); // Never needs to be refreshed.
}
});
public GroupCollection(LuckPermsService service, GroupManager manager) {
this.service = service;
this.manager = manager;
this.fallback = new SimpleCollection(service, "fallback-groups");
public SpongeGroupManager(LPSpongePlugin plugin) {
this.plugin = plugin;
this.fallback = plugin.getService().getFallbackGroupSubjects();
}
@Override
public SpongeGroup apply(String name) {
return new SpongeGroup(name, plugin);
}
/* ------------------------------------------
* Manager methods
* ------------------------------------------ */
@Override
public Map<String, SpongeGroup> getAll() {
return ImmutableMap.copyOf(objects.asMap());
}
@Override
public SpongeGroup getOrMake(String id) {
return objects.getUnchecked(id);
}
@Override
public SpongeGroup getIfLoaded(String id) {
return objects.getIfPresent(id);
}
@Override
public boolean isLoaded(String id) {
return objects.asMap().containsKey(id);
}
@Override
public void unload(Group t) {
if (t != null) {
objects.invalidate(t.getId());
}
}
@Override
public void unloadAll() {
objects.invalidateAll();
}
/* ------------------------------------------
* SubjectCollection methods
* ------------------------------------------ */
@Override
public String getIdentifier() {
return PermissionService.SUBJECTS_GROUP;
@@ -86,27 +124,33 @@ public class GroupCollection implements SubjectCollection {
@Override
public Subject get(@NonNull String id) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.GROUP_COLLECTION_GET)) {
// Special Sponge method. This call will actually load the group from the datastore if not already present.
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_COLLECTION_GET)) {
id = id.toLowerCase();
if (ArgumentChecker.checkName(id)) {
service.getPlugin().getLog().warn("Couldn't get group subject for id: " + id + " (invalid name)");
plugin.getLog().warn("Couldn't get group subject for id: " + id + " (invalid name)");
return fallback.get(id); // fallback to transient collection
}
// 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().getStorage().createAndLoadGroup(id).join();
service.getPlugin().getLog().warn("Loading '" + id + "' took " + (System.currentTimeMillis() - startTime) + " ms.");
}
// check if the group is loaded in memory.
if (isLoaded(id)) {
return getIfLoaded(id).getSpongeData();
} else {
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);
// Group isn't already loaded. hopefully this call is not on the main thread. :(
plugin.getLog().warn("Group Subject '" + id + "' was requested, but is not loaded in memory. Loading it from storage now.");
long startTime = System.currentTimeMillis();
plugin.getStorage().createAndLoadGroup(id).join();
SpongeGroup group = getIfLoaded(id);
if (group == null) {
plugin.getLog().severe("Error whilst loading group '" + id + "'.");
return fallback.get(id);
}
plugin.getLog().warn("Loading '" + id + "' took " + (System.currentTimeMillis() - startTime) + " ms.");
return group.getSpongeData();
}
}
}
@@ -114,29 +158,30 @@ public class GroupCollection implements SubjectCollection {
@Override
public boolean hasRegistered(@NonNull String id) {
id = id.toLowerCase();
return !ArgumentChecker.checkName(id) && (groups.asMap().containsKey(id) || manager.isLoaded(id));
return !ArgumentChecker.checkName(id) && isLoaded(id);
}
@Override
public Iterable<Subject> getAllSubjects() {
return groups.asMap().values().stream().collect(ImmutableCollectors.toImmutableList());
return objects.asMap().values().stream().map(SpongeGroup::getSpongeData).collect(ImmutableCollectors.toImmutableList());
}
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull String node) {
return getAllWithPermission(SubjectData.GLOBAL_CONTEXT, node);
public Map<Subject, Boolean> getAllWithPermission(@NonNull String id) {
return getAllWithPermission(SubjectData.GLOBAL_CONTEXT, id);
}
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull Set<Context> contexts, @NonNull String node) {
ContextSet cs = LuckPermsService.convertContexts(contexts);
return groups.asMap().values().stream()
return objects.asMap().values().stream()
.map(SpongeGroup::getSpongeData)
.filter(sub -> sub.getPermissionValue(cs, node) != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(cs, node).asBoolean()));
}
@Override
public Subject getDefaults() {
return service.getDefaultSubjects().get(getIdentifier());
return plugin.getService().getDefaultSubjects().get(getIdentifier());
}
}
@@ -0,0 +1,245 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.sponge.managers;
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.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.NonNull;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.core.UserIdentifier;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.managers.impl.GenericUserManager;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.model.SpongeUser;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.simple.SimpleCollection;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class SpongeUserManager implements UserManager, SubjectCollection {
private final LPSpongePlugin plugin;
private final SimpleCollection fallback;
private final LoadingCache<UserIdentifier, SpongeUser> objects = CacheBuilder.newBuilder()
.build(new CacheLoader<UserIdentifier, SpongeUser>() {
@Override
public SpongeUser load(UserIdentifier i) {
return apply(i);
}
@Override
public ListenableFuture<SpongeUser> reload(UserIdentifier i, SpongeUser t) {
return Futures.immediateFuture(t); // Never needs to be refreshed.
}
});
public SpongeUserManager(LPSpongePlugin plugin) {
this.plugin = plugin;
this.fallback = plugin.getService().getFallbackUserSubjects();
}
@Override
public SpongeUser apply(UserIdentifier id) {
return id.getUsername() == null ?
new SpongeUser(id.getUuid(), plugin) :
new SpongeUser(id.getUuid(), id.getUsername(), plugin);
}
/* ------------------------------------------
* Manager methods
* ------------------------------------------ */
@Override
public Map<UserIdentifier, SpongeUser> getAll() {
return ImmutableMap.copyOf(objects.asMap());
}
@Override
public SpongeUser getOrMake(UserIdentifier id) {
return objects.getUnchecked(id);
}
@Override
public SpongeUser getIfLoaded(UserIdentifier id) {
return objects.getIfPresent(id);
}
@Override
public boolean isLoaded(UserIdentifier id) {
return objects.asMap().containsKey(id);
}
@Override
public void unload(User t) {
// TODO override
if (t != null) {
objects.invalidate(t.getId());
}
}
@Override
public void unloadAll() {
objects.invalidateAll();
}
/* ------------------------------------------
* UserManager methods
* ------------------------------------------ */
@Override
public SpongeUser getByUsername(String name) {
for (SpongeUser user : getAll().values()) {
if (user.getName().equalsIgnoreCase(name)) {
return user;
}
}
return null;
}
@Override
public SpongeUser get(UUID uuid) {
return getIfLoaded(UserIdentifier.of(uuid, null));
}
@Override
public boolean giveDefaultIfNeeded(User user, boolean save) {
return GenericUserManager.giveDefaultIfNeeded(user, save, plugin);
}
@Override
public void cleanup(User user) {
if (!plugin.isOnline(plugin.getUuidCache().getExternalUUID(user.getUuid()))) {
unload(user);
}
}
@Override
public void updateAllUsers() {
plugin.doSync(() -> {
Set<UUID> players = plugin.getOnlinePlayers();
plugin.doAsync(() -> {
for (UUID uuid : players) {
UUID internal = plugin.getUuidCache().getUUID(uuid);
plugin.getStorage().loadUser(internal, "null").join();
}
});
});
}
/* ------------------------------------------
* SubjectCollection methods
* ------------------------------------------ */
@Override
public String getIdentifier() {
return PermissionService.SUBJECTS_USER;
}
@Override
public Subject get(@NonNull String id) {
// Special Sponge method. This call will actually load the user from the datastore if not already present.
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_COLLECTION_GET)) {
UUID uuid = Util.parseUuid(id);
if (uuid == null) {
plugin.getLog().warn("Couldn't get user subject for id: " + id + " (not a uuid)");
return fallback.get(id); // fallback to the transient collection
}
UUID u = plugin.getUuidCache().getUUID(uuid);
// check if the user is loaded in memory.
if (isLoaded(UserIdentifier.of(u, null))) {
return get(u).getSpongeData();
} else {
// User isn't already loaded. hopefully this call is not on the main thread. :(
plugin.getLog().warn("User Subject '" + u + "' was requested, but is not loaded in memory. Loading them from storage now.");
long startTime = System.currentTimeMillis();
plugin.getStorage().loadUser(u, "null").join();
SpongeUser user = get(u);
if (user == null) {
plugin.getLog().severe("Error whilst loading user '" + u + "'.");
return fallback.get(u.toString());
}
user.setupData(false);
plugin.getLog().warn("Loading '" + u + "' took " + (System.currentTimeMillis() - startTime) + " ms.");
return user.getSpongeData();
}
}
}
@Override
public boolean hasRegistered(@NonNull String id) {
UUID uuid = Util.parseUuid(id);
if (uuid == null) {
return false;
}
UUID internal = plugin.getUuidCache().getUUID(uuid);
return isLoaded(UserIdentifier.of(internal, null));
}
@Override
public Iterable<Subject> getAllSubjects() {
return objects.asMap().values().stream().map(SpongeUser::getSpongeData).collect(ImmutableCollectors.toImmutableList());
}
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull String id) {
return getAllWithPermission(SubjectData.GLOBAL_CONTEXT, id);
}
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull Set<Context> contexts, @NonNull String node) {
ContextSet cs = LuckPermsService.convertContexts(contexts);
return objects.asMap().values().stream()
.map(SpongeUser::getSpongeData)
.filter(sub -> sub.getPermissionValue(cs, node) != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(cs, node).asBoolean()));
}
@Override
public Subject getDefaults() {
return plugin.getService().getDefaultSubjects().get(getIdentifier());
}
}
@@ -90,7 +90,7 @@ public class MigrationPermissionManager extends SubCommand<Object> {
// Make a LuckPerms group for the one being migrated
plugin.getStorage().createAndLoadGroup(pmGroup.getIdentifier().toLowerCase()).join();
Group group = plugin.getGroupManager().get(pmGroup.getIdentifier().toLowerCase());
Group group = plugin.getGroupManager().getIfLoaded(pmGroup.getIdentifier().toLowerCase());
migrateSubject(pmGroup, group);
plugin.getStorage().saveGroup(group);
}
@@ -84,7 +84,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
// Make a LuckPerms group for the one being migrated
plugin.getStorage().createAndLoadGroup(pexGroup.getIdentifier().toLowerCase()).join();
Group group = plugin.getGroupManager().get(pexGroup.getIdentifier().toLowerCase());
Group group = plugin.getGroupManager().getIfLoaded(pexGroup.getIdentifier().toLowerCase());
migrateSubject(pexGroup, group);
plugin.getStorage().saveGroup(group);
}
@@ -0,0 +1,216 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.sponge.model;
import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList;
import lombok.Getter;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.MetaUtils;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.LuckPermsSubject;
import me.lucko.luckperms.sponge.service.LuckPermsSubjectData;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.permission.NodeTree;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class SpongeGroup extends Group {
@Getter
private final GroupSubject spongeData;
public SpongeGroup(String name, LPSpongePlugin plugin) {
super(name, plugin);
this.spongeData = new GroupSubject(plugin, this);
}
public static class GroupSubject extends LuckPermsSubject {
private final SpongeGroup parent;
private final LPSpongePlugin plugin;
@Getter
private final LuckPermsSubjectData subjectData;
@Getter
private final LuckPermsSubjectData transientSubjectData;
private GroupSubject(LPSpongePlugin plugin, SpongeGroup parent) {
this.parent = parent;
this.plugin = plugin;
this.subjectData = new LuckPermsSubjectData(true, plugin.getService(), parent);
this.transientSubjectData = new LuckPermsSubjectData(false, plugin.getService(), parent);
}
@Override
public String getIdentifier() {
return parent.getObjectName();
}
@Override
public Optional<CommandSource> getCommandSource() {
return Optional.empty();
}
@Override
public SubjectCollection getContainingCollection() {
return plugin.getService().getGroupSubjects();
}
@Override
public Tristate getPermissionValue(ContextSet contexts, String permission) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PERMISSION_VALUE)) {
Map<String, Boolean> permissions = parent.getAllNodesFiltered(ExtractedContexts.generate(plugin.getService().calculateContexts(contexts))).stream()
.map(LocalizedNode::getNode)
.collect(Collectors.toMap(Node::getPermission, Node::getValue));
Tristate t = NodeTree.of(permissions).get(permission);
if (t != Tristate.UNDEFINED) {
return t;
}
t = plugin.getService().getGroupSubjects().getDefaults().getPermissionValue(LuckPermsService.convertContexts(contexts), permission);
if (t != Tristate.UNDEFINED) {
return t;
}
t = plugin.getService().getDefaults().getPermissionValue(LuckPermsService.convertContexts(contexts), permission);
return t;
}
}
@Override
public boolean isChildOf(ContextSet contexts, Subject parent) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_IS_CHILD_OF)) {
return parent instanceof SpongeGroup && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean();
}
}
@Override
public List<Subject> getParents(ContextSet contexts) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PARENTS)) {
List<Subject> subjects = parent.getAllNodesFiltered(ExtractedContexts.generate(plugin.getService().calculateContexts(contexts))).stream()
.map(LocalizedNode::getNode)
.filter(Node::isGroupNode)
.map(Node::getGroupName)
.map(s -> plugin.getService().getGroupSubjects().get(s))
.collect(Collectors.toList());
subjects.addAll(plugin.getService().getGroupSubjects().getDefaults().getParents(LuckPermsService.convertContexts(contexts)));
subjects.addAll(plugin.getService().getDefaults().getParents(LuckPermsService.convertContexts(contexts)));
return ImmutableList.copyOf(subjects);
}
}
@Override
public Optional<String> getOption(ContextSet contexts, String s) {
try (Timing ignored = plugin.getService().getPlugin().getTimings().time(LPTiming.GROUP_GET_OPTION)) {
Optional<String> option;
if (s.equalsIgnoreCase("prefix")) {
option = getChatMeta(contexts, true);
} else if (s.equalsIgnoreCase("suffix")) {
option = getChatMeta(contexts, false);
} else {
option = getMeta(contexts, s);
}
if (option.isPresent()) {
return option;
}
option = plugin.getService().getGroupSubjects().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s);
if (option.isPresent()) {
return option;
}
return plugin.getService().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s);
}
}
@Override
public ContextSet getActiveContextSet() {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_ACTIVE_CONTEXTS)) {
return plugin.getContextManager().getApplicableContext(this);
}
}
private Optional<String> getChatMeta(ContextSet contexts, boolean prefix) {
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : parent.getAllNodesFiltered(ExtractedContexts.generate(plugin.getService().calculateContexts(contexts)))) {
if (!n.getValue()) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (value.getKey() > priority) {
meta = value.getValue();
priority = value.getKey();
}
}
return meta == null ? Optional.empty() : Optional.of(MetaUtils.unescapeCharacters(meta));
}
private Optional<String> getMeta(ContextSet contexts, String key) {
for (Node n : parent.getAllNodesFiltered(ExtractedContexts.generate(plugin.getService().calculateContexts(contexts)))) {
if (!n.getValue()) {
continue;
}
if (!n.isMeta()) {
continue;
}
Map.Entry<String, String> m = n.getMeta();
if (!m.getKey().equalsIgnoreCase(key)) {
continue;
}
return Optional.of(MetaUtils.unescapeCharacters(m.getValue()));
}
return Optional.empty();
}
}
}
@@ -0,0 +1,188 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.sponge.model;
import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList;
import lombok.Getter;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.LuckPermsSubject;
import me.lucko.luckperms.sponge.service.LuckPermsSubjectData;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public class SpongeUser extends User {
@Getter
private final UserSubject spongeData;
public SpongeUser(UUID uuid, LPSpongePlugin plugin) {
super(uuid, plugin);
this.spongeData = new UserSubject(plugin, this);
}
public SpongeUser(UUID uuid, String name, LPSpongePlugin plugin) {
super(uuid, name, plugin);
this.spongeData = new UserSubject(plugin, this);
}
public static class UserSubject extends LuckPermsSubject {
private final SpongeUser parent;
private final LPSpongePlugin plugin;
@Getter
private final LuckPermsSubjectData subjectData;
@Getter
private final LuckPermsSubjectData transientSubjectData;
private UserSubject(LPSpongePlugin plugin, SpongeUser parent) {
this.parent = parent;
this.plugin = plugin;
this.subjectData = new LuckPermsSubjectData(true, plugin.getService(), parent);
this.transientSubjectData = new LuckPermsSubjectData(false, plugin.getService(), parent);
}
private boolean hasData() {
return parent.getUserData() != null;
}
@Override
public String getIdentifier() {
return plugin.getUuidCache().getExternalUUID(parent.getUuid()).toString();
}
@Override
public Optional<CommandSource> getCommandSource() {
final UUID uuid = plugin.getUuidCache().getExternalUUID(parent.getUuid());
Optional<Player> p = Sponge.getServer().getPlayer(uuid);
if (p.isPresent()) {
return Optional.of(p.get());
}
return Optional.empty();
}
@Override
public SubjectCollection getContainingCollection() {
return plugin.getService().getUserSubjects();
}
@Override
public Tristate getPermissionValue(ContextSet contexts, String permission) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PERMISSION_VALUE)) {
if (!hasData()) {
return Tristate.UNDEFINED;
}
return LuckPermsService.convertTristate(parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission));
}
}
@Override
public boolean isChildOf(ContextSet contexts, Subject parent) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_IS_CHILD_OF)) {
return parent instanceof SpongeGroup && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean();
}
}
@Override
public List<Subject> getParents(ContextSet contexts) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PARENTS)) {
ImmutableList.Builder<Subject> subjects = ImmutableList.builder();
if (hasData()) {
for (String perm : parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getImmutableBacking().keySet()) {
if (!perm.startsWith("group.")) {
continue;
}
String groupName = perm.substring("group.".length());
if (plugin.getGroupManager().isLoaded(groupName)) {
subjects.add(plugin.getService().getGroupSubjects().get(groupName));
}
}
}
subjects.addAll(plugin.getService().getUserSubjects().getDefaults().getParents(LuckPermsService.convertContexts(contexts)));
subjects.addAll(plugin.getService().getDefaults().getParents(LuckPermsService.convertContexts(contexts)));
return subjects.build();
}
}
@Override
public Optional<String> getOption(ContextSet contexts, String s) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_OPTION)) {
if (hasData()) {
MetaData data = parent.getUserData().getMetaData(plugin.getService().calculateContexts(contexts));
if (s.equalsIgnoreCase("prefix")) {
if (data.getPrefix() != null) {
return Optional.of(data.getPrefix());
}
}
if (s.equalsIgnoreCase("suffix")) {
if (data.getSuffix() != null) {
return Optional.of(data.getSuffix());
}
}
if (data.getMeta().containsKey(s)) {
return Optional.of(data.getMeta().get(s));
}
}
Optional<String> v = plugin.getService().getUserSubjects().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s);
if (v.isPresent()) {
return v;
}
return plugin.getService().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s);
}
}
@Override
public ContextSet getActiveContextSet() {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_ACTIVE_CONTEXTS)) {
return plugin.getContextManager().getApplicableContext(this);
}
}
}
}
@@ -1,208 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.sponge.service;
import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.permission.NodeTree;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static me.lucko.luckperms.api.MetaUtils.unescapeCharacters;
@Getter
@EqualsAndHashCode(of = "group", callSuper = false)
public class LuckPermsGroupSubject extends LuckPermsSubject {
public static LuckPermsGroupSubject wrapGroup(Group group, LuckPermsService service) {
return new LuckPermsGroupSubject(group, service);
}
@Getter(value = AccessLevel.NONE)
private LuckPermsService service;
private Group group;
private LuckPermsSubjectData subjectData;
private LuckPermsSubjectData transientSubjectData;
private LuckPermsGroupSubject(Group group, LuckPermsService service) {
this.group = group;
this.subjectData = new LuckPermsSubjectData(true, service, group);
this.transientSubjectData = new LuckPermsSubjectData(false, service, group);
this.service = service;
}
@Override
public String getIdentifier() {
return group.getObjectName();
}
@Override
public Optional<CommandSource> getCommandSource() {
return Optional.empty();
}
@Override
public SubjectCollection getContainingCollection() {
return service.getGroupSubjects();
}
@Override
public Tristate getPermissionValue(ContextSet contexts, String permission) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.GROUP_GET_PERMISSION_VALUE)) {
Map<String, Boolean> permissions = group.getAllNodesFiltered(ExtractedContexts.generate(service.calculateContexts(contexts))).stream()
.map(LocalizedNode::getNode)
.collect(Collectors.toMap(Node::getPermission, Node::getValue));
Tristate t = NodeTree.of(permissions).get(permission);
if (t != Tristate.UNDEFINED) {
return t;
}
t = service.getGroupSubjects().getDefaults().getPermissionValue(LuckPermsService.convertContexts(contexts), permission);
if (t != Tristate.UNDEFINED) {
return t;
}
t = service.getDefaults().getPermissionValue(LuckPermsService.convertContexts(contexts), permission);
return t;
}
}
@Override
public boolean isChildOf(ContextSet contexts, Subject parent) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.GROUP_IS_CHILD_OF)) {
return parent instanceof LuckPermsGroupSubject && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean();
}
}
@Override
public List<Subject> getParents(ContextSet contexts) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.GROUP_GET_PARENTS)) {
List<Subject> subjects = group.getAllNodesFiltered(ExtractedContexts.generate(service.calculateContexts(contexts))).stream()
.map(LocalizedNode::getNode)
.filter(Node::isGroupNode)
.map(Node::getGroupName)
.map(s -> service.getGroupSubjects().get(s))
.collect(Collectors.toList());
subjects.addAll(service.getGroupSubjects().getDefaults().getParents(LuckPermsService.convertContexts(contexts)));
subjects.addAll(service.getDefaults().getParents(LuckPermsService.convertContexts(contexts)));
return ImmutableList.copyOf(subjects);
}
}
@Override
public Optional<String> getOption(ContextSet contexts, String s) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.GROUP_GET_OPTION)) {
Optional<String> option;
if (s.equalsIgnoreCase("prefix")) {
option = getChatMeta(contexts, true);
} else if (s.equalsIgnoreCase("suffix")) {
option = getChatMeta(contexts, false);
} else {
option = getMeta(contexts, s);
}
if (option.isPresent()) {
return option;
}
option = service.getGroupSubjects().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s);
if (option.isPresent()) {
return option;
}
return service.getDefaults().getOption(LuckPermsService.convertContexts(contexts), s);
}
}
@Override
public ContextSet getActiveContextSet() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.GROUP_GET_ACTIVE_CONTEXTS)) {
return service.getPlugin().getContextManager().getApplicableContext(this);
}
}
private Optional<String> getChatMeta(ContextSet contexts, boolean prefix) {
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : group.getAllNodesFiltered(ExtractedContexts.generate(service.calculateContexts(contexts)))) {
if (!n.getValue()) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (value.getKey() > priority) {
meta = value.getValue();
priority = value.getKey();
}
}
return meta == null ? Optional.empty() : Optional.of(unescapeCharacters(meta));
}
private Optional<String> getMeta(ContextSet contexts, String key) {
for (Node n : group.getAllNodesFiltered(ExtractedContexts.generate(service.calculateContexts(contexts)))) {
if (!n.getValue()) {
continue;
}
if (!n.isMeta()) {
continue;
}
Map.Entry<String, String> m = n.getMeta();
if (!m.getKey().equalsIgnoreCase(key)) {
continue;
}
return Optional.of(m.getValue());
}
return Optional.empty();
}
}
@@ -29,13 +29,15 @@ import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.*;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.contexts.SpongeCalculatorLink;
import me.lucko.luckperms.sponge.service.collections.GroupCollection;
import me.lucko.luckperms.sponge.service.collections.UserCollection;
import me.lucko.luckperms.sponge.managers.SpongeGroupManager;
import me.lucko.luckperms.sponge.managers.SpongeUserManager;
import me.lucko.luckperms.sponge.service.persisted.PersistedCollection;
import me.lucko.luckperms.sponge.service.persisted.SubjectStorage;
import me.lucko.luckperms.sponge.service.simple.SimpleCollection;
@@ -61,8 +63,10 @@ public class LuckPermsService implements PermissionService {
private final LPSpongePlugin plugin;
private final SubjectStorage storage;
private final UserCollection userSubjects;
private final GroupCollection groupSubjects;
private final SpongeUserManager userSubjects;
private final SimpleCollection fallbackUserSubjects;
private final SpongeGroupManager groupSubjects;
private final SimpleCollection fallbackGroupSubjects;
private final PersistedCollection defaultSubjects;
private final Set<PermissionDescription> descriptionSet;
@@ -73,6 +77,11 @@ public class LuckPermsService implements PermissionService {
public SubjectCollection load(String s) {
return new SimpleCollection(LuckPermsService.this, s);
}
@Override
public ListenableFuture<SubjectCollection> reload(String s, SubjectCollection collection) {
return Futures.immediateFuture(collection); // Never needs to be refreshed.
}
});
public LuckPermsService(LPSpongePlugin plugin) {
@@ -80,13 +89,17 @@ public class LuckPermsService implements PermissionService {
storage = new SubjectStorage(new File(plugin.getDataFolder(), "local"));
userSubjects = new UserCollection(this, plugin.getUserManager());
groupSubjects = new GroupCollection(this, plugin.getGroupManager());
userSubjects = plugin.getUserManager();
fallbackUserSubjects = new SimpleCollection(this, "fallback-users");
groupSubjects = plugin.getGroupManager();
fallbackGroupSubjects = new SimpleCollection(this, "fallback-groups");
defaultSubjects = new PersistedCollection(this, "defaults");
defaultSubjects.loadAll();
collections.put(PermissionService.SUBJECTS_USER, userSubjects);
collections.put("fallback-users", fallbackUserSubjects);
collections.put(PermissionService.SUBJECTS_GROUP, groupSubjects);
collections.put("fallback-groups", fallbackGroupSubjects);
collections.put("defaults", defaultSubjects);
descriptionSet = ConcurrentHashMap.newKeySet();
@@ -38,6 +38,7 @@ import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.sponge.model.SpongeGroup;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
@@ -252,17 +253,17 @@ public class LuckPermsSubjectData implements SubjectData {
@Override
public boolean addParent(@NonNull Set<Context> set, @NonNull Subject subject) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_ADD_PARENT)) {
if (subject instanceof LuckPermsGroupSubject) {
LuckPermsGroupSubject permsSubject = ((LuckPermsGroupSubject) subject);
if (subject instanceof SpongeGroup) {
SpongeGroup permsSubject = ((SpongeGroup) subject);
ContextSet contexts = LuckPermsService.convertContexts(set);
try {
if (enduring) {
holder.setPermission(new NodeBuilder("group." + permsSubject.getIdentifier())
holder.setPermission(new NodeBuilder("group." + permsSubject.getName())
.withExtraContext(contexts)
.build());
} else {
holder.setTransientPermission(new NodeBuilder("group." + permsSubject.getIdentifier())
holder.setTransientPermission(new NodeBuilder("group." + permsSubject.getName())
.withExtraContext(contexts)
.build());
}
@@ -278,17 +279,17 @@ public class LuckPermsSubjectData implements SubjectData {
@Override
public boolean removeParent(@NonNull Set<Context> set, @NonNull Subject subject) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_REMOVE_PARENT)) {
if (subject instanceof LuckPermsGroupSubject) {
LuckPermsGroupSubject permsSubject = ((LuckPermsGroupSubject) subject);
if (subject instanceof SpongeGroup) {
SpongeGroup permsSubject = ((SpongeGroup) subject);
ContextSet contexts = LuckPermsService.convertContexts(set);
try {
if (enduring) {
holder.unsetPermission(new NodeBuilder("group." + permsSubject.getIdentifier())
holder.unsetPermission(new NodeBuilder("group." + permsSubject.getName())
.withExtraContext(contexts)
.build());
} else {
holder.unsetTransientPermission(new NodeBuilder("group." + permsSubject.getIdentifier())
holder.unsetTransientPermission(new NodeBuilder("group." + permsSubject.getName())
.withExtraContext(contexts)
.build());
}
@@ -1,172 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.sponge.service;
import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Getter
@EqualsAndHashCode(of = "user", callSuper = false)
public class LuckPermsUserSubject extends LuckPermsSubject {
public static LuckPermsUserSubject wrapUser(User user, LuckPermsService service) {
return new LuckPermsUserSubject(user, service);
}
@Getter(value = AccessLevel.NONE)
private LuckPermsService service;
private User user;
private LuckPermsSubjectData subjectData;
private LuckPermsSubjectData transientSubjectData;
private LuckPermsUserSubject(User user, LuckPermsService service) {
this.user = user;
this.service = service;
this.subjectData = new LuckPermsSubjectData(true, service, user);
this.transientSubjectData = new LuckPermsSubjectData(false, service, user);
}
private boolean hasData() {
return user.getUserData() != null;
}
@Override
public String getIdentifier() {
return service.getPlugin().getUuidCache().getExternalUUID(user.getUuid()).toString();
}
@Override
public Optional<CommandSource> getCommandSource() {
final UUID uuid = service.getPlugin().getUuidCache().getExternalUUID(user.getUuid());
Optional<Player> p = Sponge.getServer().getPlayer(uuid);
if (p.isPresent()) {
return Optional.of(p.get());
}
return Optional.empty();
}
@Override
public SubjectCollection getContainingCollection() {
return service.getUserSubjects();
}
@Override
public Tristate getPermissionValue(ContextSet contexts, String permission) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.USER_GET_PERMISSION_VALUE)) {
return !hasData() ?
Tristate.UNDEFINED :
LuckPermsService.convertTristate(
user.getUserData().getPermissionData(service.calculateContexts(contexts)).getPermissionValue(permission)
);
}
}
@Override
public boolean isChildOf(ContextSet contexts, Subject parent) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.USER_IS_CHILD_OF)) {
return parent instanceof LuckPermsGroupSubject && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean();
}
}
@Override
public List<Subject> getParents(ContextSet contexts) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.USER_GET_PARENTS)) {
ImmutableList.Builder<Subject> subjects = ImmutableList.builder();
if (hasData()) {
for (String perm : user.getUserData().getPermissionData(service.calculateContexts(contexts)).getImmutableBacking().keySet()) {
if (!perm.startsWith("group.")) {
continue;
}
String groupName = perm.substring("group.".length());
if (service.getPlugin().getGroupManager().isLoaded(groupName)) {
subjects.add(service.getGroupSubjects().get(groupName));
}
}
}
subjects.addAll(service.getUserSubjects().getDefaults().getParents(LuckPermsService.convertContexts(contexts)));
subjects.addAll(service.getDefaults().getParents(LuckPermsService.convertContexts(contexts)));
return subjects.build();
}
}
@Override
public Optional<String> getOption(ContextSet contexts, String s) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.USER_GET_OPTION)) {
if (hasData()) {
MetaData data = user.getUserData().getMetaData(service.calculateContexts(contexts));
if (s.equalsIgnoreCase("prefix")) {
if (data.getPrefix() != null) {
return Optional.of(data.getPrefix());
}
}
if (s.equalsIgnoreCase("suffix")) {
if (data.getSuffix() != null) {
return Optional.of(data.getSuffix());
}
}
if (data.getMeta().containsKey(s)) {
return Optional.of(data.getMeta().get(s));
}
}
Optional<String> v = service.getUserSubjects().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s);
if (v.isPresent()) {
return v;
}
return service.getDefaults().getOption(LuckPermsService.convertContexts(contexts), s);
}
}
@Override
public ContextSet getActiveContextSet() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.USER_GET_ACTIVE_CONTEXTS)) {
return service.getPlugin().getContextManager().getApplicableContext(this);
}
}
}
@@ -1,163 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.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.commands.utils.Util;
import me.lucko.luckperms.common.core.UserIdentifier;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.LuckPermsUserSubject;
import me.lucko.luckperms.sponge.service.simple.SimpleCollection;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
/**
* Manages low level Subject instances for the PermissionService.
* Most calls are cached.
*/
public class UserCollection implements SubjectCollection {
private final LuckPermsService service;
private final UserManager manager;
private final SimpleCollection fallback;
private final LoadingCache<UUID, LuckPermsUserSubject> users = CacheBuilder.newBuilder()
.build(new CacheLoader<UUID, LuckPermsUserSubject>() {
@Override
public LuckPermsUserSubject load(UUID uuid) throws Exception {
User user = manager.get(uuid);
if (user == null) {
throw new IllegalStateException("User not loaded");
}
return LuckPermsUserSubject.wrapUser(user, service);
}
@Override
public ListenableFuture<LuckPermsUserSubject> reload(UUID uuid, LuckPermsUserSubject s) {
return Futures.immediateFuture(s); // Never needs to be refreshed.
}
});
public UserCollection(LuckPermsService service, UserManager manager) {
this.service = service;
this.manager = manager;
this.fallback = new SimpleCollection(service, "fallback-users");
}
@Override
public String getIdentifier() {
return PermissionService.SUBJECTS_USER;
}
public void unload(UUID uuid) {
users.invalidate(uuid);
}
@Override
public Subject get(@NonNull String id) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.USER_COLLECTION_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
}
UUID u = service.getPlugin().getUuidCache().getUUID(uuid);
// 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().getStorage().loadUser(u, "null").join();
User user = service.getPlugin().getUserManager().get(u);
if (user != null) {
user.setupData(false);
}
service.getPlugin().getLog().warn("Loading '" + u + "' took " + (System.currentTimeMillis() - startTime) + " ms.");
}
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());
}
}
}
@Override
public boolean hasRegistered(@NonNull String id) {
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
public Iterable<Subject> getAllSubjects() {
return users.asMap().values().stream().collect(ImmutableCollectors.toImmutableList());
}
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull String id) {
return getAllWithPermission(SubjectData.GLOBAL_CONTEXT, id);
}
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull Set<Context> contexts, @NonNull String node) {
ContextSet cs = LuckPermsService.convertContexts(contexts);
return users.asMap().values().stream()
.filter(sub -> sub.getPermissionValue(cs, node) != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(cs, node).asBoolean()));
}
@Override
public Subject getDefaults() {
return service.getDefaultSubjects().get(getIdentifier());
}
}