WIP: Sponge service refactor

This commit is contained in:
Luck 2016-12-02 17:20:36 +00:00
parent 02ff4a4acb
commit 2b8bbc434f
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
26 changed files with 1151 additions and 618 deletions

View File

@ -34,7 +34,6 @@ import me.lucko.luckperms.common.calculators.processors.WildcardProcessor;
import me.lucko.luckperms.common.core.model.User; import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.sponge.calculators.DefaultsProcessor; import me.lucko.luckperms.sponge.calculators.DefaultsProcessor;
import me.lucko.luckperms.sponge.calculators.SpongeWildcardProcessor; import me.lucko.luckperms.sponge.calculators.SpongeWildcardProcessor;
import me.lucko.luckperms.sponge.service.LuckPermsService;
@AllArgsConstructor @AllArgsConstructor
public class SpongeCalculatorFactory extends AbstractCalculatorFactory { public class SpongeCalculatorFactory extends AbstractCalculatorFactory {
@ -51,7 +50,7 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory {
if (plugin.getConfiguration().isApplyingRegex()) { if (plugin.getConfiguration().isApplyingRegex()) {
processors.add(new RegexProcessor()); processors.add(new RegexProcessor());
} }
processors.add(new DefaultsProcessor(plugin.getService(), LuckPermsService.convertContexts(contexts.getContexts()))); processors.add(new DefaultsProcessor(plugin.getService(), contexts.getContexts()));
return registerCalculator(new PermissionCalculator(plugin, user.getName(), processors.build())); return registerCalculator(new PermissionCalculator(plugin, user.getName(), processors.build()));
} }

View File

@ -23,34 +23,31 @@
package me.lucko.luckperms.sponge.calculators; package me.lucko.luckperms.sponge.calculators;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.calculators.PermissionProcessor; import me.lucko.luckperms.common.calculators.PermissionProcessor;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.util.Tristate;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static me.lucko.luckperms.sponge.service.LuckPermsService.convertTristate;
@AllArgsConstructor @AllArgsConstructor
public class DefaultsProcessor implements PermissionProcessor { public class DefaultsProcessor implements PermissionProcessor {
private final LuckPermsService service; private final LuckPermsService service;
private final Set<Context> contexts; private final ContextSet contexts;
@Override @Override
public me.lucko.luckperms.api.Tristate hasPermission(String permission) { public Tristate hasPermission(String permission) {
Tristate t = service.getUserSubjects().getDefaults().getPermissionValue(contexts, permission); Tristate t = service.getUserSubjects().getDefaultSubject().resolve(service).getPermissionValue(contexts, permission);
if (t != Tristate.UNDEFINED) { if (t != Tristate.UNDEFINED) {
return convertTristate(t); return t;
} }
Tristate t2 = service.getDefaults().getPermissionValue(contexts, permission); Tristate t2 = service.getDefaults().getPermissionValue(contexts, permission);
if (t2 != Tristate.UNDEFINED) { if (t2 != Tristate.UNDEFINED) {
return convertTristate(t); return t;
} }
return me.lucko.luckperms.api.Tristate.UNDEFINED; return Tristate.UNDEFINED;
} }
@Override @Override

View File

@ -25,7 +25,7 @@ package me.lucko.luckperms.sponge.contexts;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.context.ContextCalculator; import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet; import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.base.Util;
import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.Subject;
@ -38,11 +38,11 @@ public class SpongeCalculatorLink extends ContextCalculator<Subject> {
@Override @Override
public MutableContextSet giveApplicableContext(Subject subject, MutableContextSet accumulator) { public MutableContextSet giveApplicableContext(Subject subject, MutableContextSet accumulator) {
Set<Context> contexts = LuckPermsService.convertContexts(accumulator); Set<Context> contexts = Util.convertContexts(accumulator);
calculator.accumulateContexts(subject, contexts); calculator.accumulateContexts(subject, contexts);
accumulator.clear(); accumulator.clear();
accumulator.addAll(LuckPermsService.convertContexts(contexts)); accumulator.addAll(Util.convertContexts(contexts));
return accumulator; return accumulator;
} }

View File

@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.NonNull; import lombok.NonNull;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.model.Group; import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.managers.GroupManager; import me.lucko.luckperms.common.managers.GroupManager;
@ -38,18 +39,16 @@ import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.model.SpongeGroup; import me.lucko.luckperms.sponge.model.SpongeGroup;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.base.LPSubject;
import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming; 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.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.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set;
public class SpongeGroupManager implements GroupManager, SubjectCollection { public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
private final LPSpongePlugin plugin; private final LPSpongePlugin plugin;
private final LoadingCache<String, SpongeGroup> objects = CacheBuilder.newBuilder() private final LoadingCache<String, SpongeGroup> objects = CacheBuilder.newBuilder()
@ -120,7 +119,12 @@ public class SpongeGroupManager implements GroupManager, SubjectCollection {
} }
@Override @Override
public Subject get(@NonNull String id) { public LuckPermsService getService() {
return plugin.getService();
}
@Override
public LPSubject get(@NonNull String id) {
// Special Sponge method. This call will actually load the group from the datastore if not already present. // 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)) { try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_COLLECTION_GET)) {
@ -159,26 +163,20 @@ public class SpongeGroupManager implements GroupManager, SubjectCollection {
} }
@Override @Override
public Iterable<Subject> getAllSubjects() { public Collection<LPSubject> getSubjects() {
return objects.asMap().values().stream().map(SpongeGroup::getSpongeData).collect(ImmutableCollectors.toImmutableList()); return objects.asMap().values().stream().map(SpongeGroup::getSpongeData).collect(ImmutableCollectors.toImmutableList());
} }
@Override @Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull String id) { public Map<LPSubject, Boolean> getWithPermission(@NonNull ContextSet contexts, @NonNull String node) {
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() return objects.asMap().values().stream()
.map(SpongeGroup::getSpongeData) .map(SpongeGroup::getSpongeData)
.filter(sub -> sub.getPermissionValue(cs, node) != Tristate.UNDEFINED) .filter(sub -> sub.getPermissionValue(contexts, node) != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(cs, node).asBoolean())); .collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(contexts, node).asBoolean()));
} }
@Override @Override
public Subject getDefaults() { public SubjectReference getDefaultSubject() {
return plugin.getService().getDefaultSubjects().get(getIdentifier()); return SubjectReference.of("defaults", getIdentifier());
} }
} }

View File

@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.NonNull; import lombok.NonNull;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.commands.utils.Util; import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.core.UserIdentifier; import me.lucko.luckperms.common.core.UserIdentifier;
@ -40,19 +41,18 @@ import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.model.SpongeUser; import me.lucko.luckperms.sponge.model.SpongeUser;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.base.LPSubject;
import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming; 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.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.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class SpongeUserManager implements UserManager, SubjectCollection { public class SpongeUserManager implements UserManager, LPSubjectCollection {
private final LPSpongePlugin plugin; private final LPSpongePlugin plugin;
private final LoadingCache<UserIdentifier, SpongeUser> objects = CacheBuilder.newBuilder() private final LoadingCache<UserIdentifier, SpongeUser> objects = CacheBuilder.newBuilder()
@ -170,7 +170,12 @@ public class SpongeUserManager implements UserManager, SubjectCollection {
} }
@Override @Override
public Subject get(@NonNull String id) { public LuckPermsService getService() {
return plugin.getService();
}
@Override
public LPSubject get(@NonNull String id) {
// Special Sponge method. This call will actually load the user from the datastore if not already present. // 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)) { try (Timing ignored = plugin.getTimings().time(LPTiming.USER_COLLECTION_GET)) {
@ -217,26 +222,20 @@ public class SpongeUserManager implements UserManager, SubjectCollection {
} }
@Override @Override
public Iterable<Subject> getAllSubjects() { public Collection<LPSubject> getSubjects() {
return objects.asMap().values().stream().map(SpongeUser::getSpongeData).collect(ImmutableCollectors.toImmutableList()); return objects.asMap().values().stream().map(SpongeUser::getSpongeData).collect(ImmutableCollectors.toImmutableList());
} }
@Override @Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull String id) { public Map<LPSubject, Boolean> getWithPermission(@NonNull ContextSet contexts, @NonNull String node) {
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() return objects.asMap().values().stream()
.map(SpongeUser::getSpongeData) .map(SpongeUser::getSpongeData)
.filter(sub -> sub.getPermissionValue(cs, node) != Tristate.UNDEFINED) .filter(sub -> sub.getPermissionValue(contexts, node) != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(cs, node).asBoolean())); .collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(contexts, node).asBoolean()));
} }
@Override @Override
public Subject getDefaults() { public SubjectReference getDefaultSubject() {
return plugin.getService().getDefaultSubjects().get(getIdentifier()); return SubjectReference.of("defaults", getIdentifier());
} }
} }

View File

@ -29,7 +29,7 @@ import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.PermissionHolder; import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.utils.ExtractedContexts; import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.base.Util;
import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.Subject;
@ -47,7 +47,7 @@ public class MigrationUtils {
// Migrate permissions // Migrate permissions
Map<Set<Context>, Map<String, Boolean>> perms = subject.getSubjectData().getAllPermissions(); Map<Set<Context>, Map<String, Boolean>> perms = subject.getSubjectData().getAllPermissions();
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : perms.entrySet()) { for (Map.Entry<Set<Context>, Map<String, Boolean>> e : perms.entrySet()) {
ContextSet context = LuckPermsService.convertContexts(e.getKey()); ContextSet context = Util.convertContexts(e.getKey());
ExtractedContexts extractedContexts = ExtractedContexts.generate(context); ExtractedContexts extractedContexts = ExtractedContexts.generate(context);
ContextSet contexts = extractedContexts.getContextSet(); ContextSet contexts = extractedContexts.getContextSet();
@ -65,7 +65,7 @@ public class MigrationUtils {
try { try {
Map<Set<Context>, Map<String, String>> opts = subject.getSubjectData().getAllOptions(); Map<Set<Context>, Map<String, String>> opts = subject.getSubjectData().getAllOptions();
for (Map.Entry<Set<Context>, Map<String, String>> e : opts.entrySet()) { for (Map.Entry<Set<Context>, Map<String, String>> e : opts.entrySet()) {
ContextSet context = LuckPermsService.convertContexts(e.getKey()); ContextSet context = Util.convertContexts(e.getKey());
ExtractedContexts extractedContexts = ExtractedContexts.generate(context); ExtractedContexts extractedContexts = ExtractedContexts.generate(context);
ContextSet contexts = extractedContexts.getContextSet(); ContextSet contexts = extractedContexts.getContextSet();
@ -96,7 +96,7 @@ public class MigrationUtils {
// Migrate parents // Migrate parents
Map<Set<Context>, List<Subject>> parents = subject.getSubjectData().getAllParents(); Map<Set<Context>, List<Subject>> parents = subject.getSubjectData().getAllParents();
for (Map.Entry<Set<Context>, List<Subject>> e : parents.entrySet()) { for (Map.Entry<Set<Context>, List<Subject>> e : parents.entrySet()) {
ContextSet context = LuckPermsService.convertContexts(e.getKey()); ContextSet context = Util.convertContexts(e.getKey());
ExtractedContexts extractedContexts = ExtractedContexts.generate(context); ExtractedContexts extractedContexts = ExtractedContexts.generate(context);
ContextSet contexts = extractedContexts.getContextSet(); ContextSet contexts = extractedContexts.getContextSet();

View File

@ -23,28 +23,30 @@
package me.lucko.luckperms.sponge.model; package me.lucko.luckperms.sponge.model;
import co.aikar.timings.Timing; import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet;
import lombok.Getter; import lombok.Getter;
import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.MetaUtils; import me.lucko.luckperms.api.MetaUtils;
import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.model.Group; import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.utils.ExtractedContexts; import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService; 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.service.LuckPermsSubjectData;
import me.lucko.luckperms.sponge.service.base.LPSubject;
import me.lucko.luckperms.sponge.service.base.Util;
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming; import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.permission.NodeTree; import org.spongepowered.api.service.permission.NodeTree;
import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class SpongeGroup extends Group { public class SpongeGroup extends Group {
@ -57,7 +59,7 @@ public class SpongeGroup extends Group {
this.spongeData = new GroupSubject(plugin, this); this.spongeData = new GroupSubject(plugin, this);
} }
public static class GroupSubject extends LuckPermsSubject { public static class GroupSubject implements LPSubject {
private final SpongeGroup parent; private final SpongeGroup parent;
private final LPSpongePlugin plugin; private final LPSpongePlugin plugin;
@ -79,59 +81,71 @@ public class SpongeGroup extends Group {
return parent.getObjectName(); return parent.getObjectName();
} }
@Override
public Optional<String> getFriendlyIdentifier() {
return Optional.of(parent.getFriendlyName());
}
@Override @Override
public Optional<CommandSource> getCommandSource() { public Optional<CommandSource> getCommandSource() {
return Optional.empty(); return Optional.empty();
} }
@Override @Override
public SubjectCollection getContainingCollection() { public SubjectCollectionReference getParentCollection() {
return plugin.getService().getGroupSubjects(); return plugin.getService().getGroupSubjects().toReference();
}
@Override
public LuckPermsService getService() {
return plugin.getService();
} }
@Override @Override
public Tristate getPermissionValue(ContextSet contexts, String permission) { public Tristate getPermissionValue(ContextSet contexts, String permission) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PERMISSION_VALUE)) { try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PERMISSION_VALUE)) {
// TODO move this away from NodeTree
Map<String, Boolean> permissions = parent.getAllNodesFiltered(ExtractedContexts.generate(plugin.getService().calculateContexts(contexts))).stream() Map<String, Boolean> permissions = parent.getAllNodesFiltered(ExtractedContexts.generate(plugin.getService().calculateContexts(contexts))).stream()
.map(LocalizedNode::getNode) .map(LocalizedNode::getNode)
.collect(Collectors.toMap(Node::getPermission, Node::getValue)); .collect(Collectors.toMap(Node::getPermission, Node::getValue));
Tristate t = NodeTree.of(permissions).get(permission); Tristate t = Util.convertTristate(NodeTree.of(permissions).get(permission));
if (t != Tristate.UNDEFINED) { if (t != Tristate.UNDEFINED) {
return t; return t;
} }
t = plugin.getService().getGroupSubjects().getDefaults().getPermissionValue(LuckPermsService.convertContexts(contexts), permission); t = plugin.getService().getGroupSubjects().getDefaultSubject().resolve(getService()).getPermissionValue(contexts, permission);
if (t != Tristate.UNDEFINED) { if (t != Tristate.UNDEFINED) {
return t; return t;
} }
t = plugin.getService().getDefaults().getPermissionValue(LuckPermsService.convertContexts(contexts), permission); t = plugin.getService().getDefaults().getPermissionValue(contexts, permission);
return t; return t;
} }
} }
@Override @Override
public boolean isChildOf(ContextSet contexts, Subject parent) { public boolean isChildOf(ContextSet contexts, SubjectReference parent) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_IS_CHILD_OF)) { try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_IS_CHILD_OF)) {
return parent instanceof SpongeGroup && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean(); return parent.getCollection().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean();
} }
} }
@Override @Override
public List<Subject> getParents(ContextSet contexts) { public Set<SubjectReference> getParents(ContextSet contexts) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PARENTS)) { try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PARENTS)) {
List<Subject> subjects = parent.getAllNodesFiltered(ExtractedContexts.generate(plugin.getService().calculateContexts(contexts))).stream() Set<SubjectReference> subjects = parent.getAllNodesFiltered(ExtractedContexts.generate(plugin.getService().calculateContexts(contexts))).stream()
.map(LocalizedNode::getNode) .map(LocalizedNode::getNode)
.filter(Node::isGroupNode) .filter(Node::isGroupNode)
.map(Node::getGroupName) .map(Node::getGroupName)
.map(s -> plugin.getService().getGroupSubjects().get(s)) .map(s -> plugin.getService().getGroupSubjects().get(s))
.collect(Collectors.toList()); .map(LPSubject::toReference)
.collect(Collectors.toSet());
subjects.addAll(plugin.getService().getGroupSubjects().getDefaults().getParents(LuckPermsService.convertContexts(contexts))); subjects.addAll(plugin.getService().getGroupSubjects().getDefaultSubject().resolve(getService()).getParents(contexts));
subjects.addAll(plugin.getService().getDefaults().getParents(LuckPermsService.convertContexts(contexts))); subjects.addAll(plugin.getService().getDefaults().getParents(contexts));
return ImmutableList.copyOf(subjects); return ImmutableSet.copyOf(subjects);
} }
} }
@ -153,12 +167,12 @@ public class SpongeGroup extends Group {
return option; return option;
} }
option = plugin.getService().getGroupSubjects().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s); option = plugin.getService().getGroupSubjects().getDefaultSubject().resolve(getService()).getOption(contexts, s);
if (option.isPresent()) { if (option.isPresent()) {
return option; return option;
} }
return plugin.getService().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s); return plugin.getService().getDefaults().getOption(contexts, s);
} }
} }

View File

@ -23,25 +23,26 @@
package me.lucko.luckperms.sponge.model; package me.lucko.luckperms.sponge.model;
import co.aikar.timings.Timing; import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet;
import lombok.Getter; import lombok.Getter;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.caching.MetaData; import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.model.User; import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService; 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.service.LuckPermsSubjectData;
import me.lucko.luckperms.sponge.service.base.LPSubject;
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming; import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.Sponge; import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class SpongeUser extends User { public class SpongeUser extends User {
@ -59,7 +60,7 @@ public class SpongeUser extends User {
this.spongeData = new UserSubject(plugin, this); this.spongeData = new UserSubject(plugin, this);
} }
public static class UserSubject extends LuckPermsSubject { public static class UserSubject implements LPSubject {
private final SpongeUser parent; private final SpongeUser parent;
private final LPSpongePlugin plugin; private final LPSpongePlugin plugin;
@ -98,8 +99,13 @@ public class SpongeUser extends User {
} }
@Override @Override
public SubjectCollection getContainingCollection() { public SubjectCollectionReference getParentCollection() {
return plugin.getService().getUserSubjects(); return plugin.getService().getUserSubjects().toReference();
}
@Override
public LuckPermsService getService() {
return plugin.getService();
} }
@Override @Override
@ -109,21 +115,21 @@ public class SpongeUser extends User {
return Tristate.UNDEFINED; return Tristate.UNDEFINED;
} }
return LuckPermsService.convertTristate(parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission)); return parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission);
} }
} }
@Override @Override
public boolean isChildOf(ContextSet contexts, Subject parent) { public boolean isChildOf(ContextSet contexts, SubjectReference parent) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_IS_CHILD_OF)) { try (Timing ignored = plugin.getTimings().time(LPTiming.USER_IS_CHILD_OF)) {
return parent instanceof SpongeGroup && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean(); return parent.getCollection().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean();
} }
} }
@Override @Override
public List<Subject> getParents(ContextSet contexts) { public Set<SubjectReference> getParents(ContextSet contexts) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PARENTS)) { try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PARENTS)) {
ImmutableList.Builder<Subject> subjects = ImmutableList.builder(); ImmutableSet.Builder<SubjectReference> subjects = ImmutableSet.builder();
if (hasData()) { if (hasData()) {
for (String perm : parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getImmutableBacking().keySet()) { for (String perm : parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getImmutableBacking().keySet()) {
@ -133,13 +139,13 @@ public class SpongeUser extends User {
String groupName = perm.substring("group.".length()); String groupName = perm.substring("group.".length());
if (plugin.getGroupManager().isLoaded(groupName)) { if (plugin.getGroupManager().isLoaded(groupName)) {
subjects.add(plugin.getService().getGroupSubjects().get(groupName)); subjects.add(plugin.getService().getGroupSubjects().get(groupName).toReference());
} }
} }
} }
subjects.addAll(plugin.getService().getUserSubjects().getDefaults().getParents(LuckPermsService.convertContexts(contexts))); subjects.addAll(plugin.getService().getUserSubjects().getDefaultSubject().resolve(getService()).getParents(contexts));
subjects.addAll(plugin.getService().getDefaults().getParents(LuckPermsService.convertContexts(contexts))); subjects.addAll(plugin.getService().getDefaults().getParents(contexts));
return subjects.build(); return subjects.build();
} }
@ -167,12 +173,12 @@ public class SpongeUser extends User {
} }
} }
Optional<String> v = plugin.getService().getUserSubjects().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s); Optional<String> v = plugin.getService().getUserSubjects().getDefaultSubject().resolve(getService()).getOption(contexts, s);
if (v.isPresent()) { if (v.isPresent()) {
return v; return v;
} }
return plugin.getService().getDefaults().getOption(LuckPermsService.convertContexts(contexts), s); return plugin.getService().getDefaults().getOption(contexts, s);
} }
} }
@ -184,5 +190,4 @@ public class SpongeUser extends User {
} }
} }
} }

View File

@ -28,31 +28,40 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps; import com.google.common.collect.MapMaker;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.*; import lombok.*;
import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.contexts.SpongeCalculatorLink; import me.lucko.luckperms.sponge.contexts.SpongeCalculatorLink;
import me.lucko.luckperms.sponge.managers.SpongeGroupManager; import me.lucko.luckperms.sponge.managers.SpongeGroupManager;
import me.lucko.luckperms.sponge.managers.SpongeUserManager; import me.lucko.luckperms.sponge.managers.SpongeUserManager;
import me.lucko.luckperms.sponge.service.base.LPSubject;
import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.base.LPSubjectData;
import me.lucko.luckperms.sponge.service.calculated.OptionLookup;
import me.lucko.luckperms.sponge.service.calculated.PermissionLookup;
import me.lucko.luckperms.sponge.service.persisted.PersistedCollection; import me.lucko.luckperms.sponge.service.persisted.PersistedCollection;
import me.lucko.luckperms.sponge.service.persisted.SubjectStorage; import me.lucko.luckperms.sponge.service.persisted.SubjectStorage;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.service.simple.SimpleCollection; import me.lucko.luckperms.sponge.service.simple.SimpleCollection;
import me.lucko.luckperms.sponge.timings.LPTiming; import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.context.ContextCalculator; import org.spongepowered.api.service.context.ContextCalculator;
import org.spongepowered.api.service.permission.*; import org.spongepowered.api.service.permission.PermissionDescription;
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.text.Text; import org.spongepowered.api.text.Text;
import org.spongepowered.api.util.Tristate;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/** /**
* The LuckPerms implementation of the Sponge Permission Service * The LuckPerms implementation of the Sponge Permission Service
@ -70,16 +79,20 @@ public class LuckPermsService implements PermissionService {
private final PersistedCollection defaultSubjects; private final PersistedCollection defaultSubjects;
private final Set<PermissionDescription> descriptionSet; private final Set<PermissionDescription> descriptionSet;
private final Set<LoadingCache<PermissionLookup, Tristate>> localPermissionCaches;
private final Set<LoadingCache<Set<Context>, List<SubjectReference>>> localParentCaches;
private final Set<LoadingCache<OptionLookup, Optional<String>>> localOptionCaches;
@Getter(value = AccessLevel.NONE) @Getter(value = AccessLevel.NONE)
private final LoadingCache<String, SubjectCollection> collections = CacheBuilder.newBuilder() private final LoadingCache<String, LPSubjectCollection> collections = CacheBuilder.newBuilder()
.build(new CacheLoader<String, SubjectCollection>() { .build(new CacheLoader<String, LPSubjectCollection>() {
@Override @Override
public SubjectCollection load(String s) { public LPSubjectCollection load(String s) {
return new SimpleCollection(LuckPermsService.this, s); return new SimpleCollection(LuckPermsService.this, s);
} }
@Override @Override
public ListenableFuture<SubjectCollection> reload(String s, SubjectCollection collection) { public ListenableFuture<LPSubjectCollection> reload(String s, LPSubjectCollection collection) {
return Futures.immediateFuture(collection); // Never needs to be refreshed. return Futures.immediateFuture(collection); // Never needs to be refreshed.
} }
}); });
@ -87,6 +100,10 @@ public class LuckPermsService implements PermissionService {
public LuckPermsService(LPSpongePlugin plugin) { public LuckPermsService(LPSpongePlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
localPermissionCaches = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
localParentCaches = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
localOptionCaches = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
storage = new SubjectStorage(new File(plugin.getDataFolder(), "local")); storage = new SubjectStorage(new File(plugin.getDataFolder(), "local"));
userSubjects = plugin.getUserManager(); userSubjects = plugin.getUserManager();
@ -105,25 +122,30 @@ public class LuckPermsService implements PermissionService {
descriptionSet = ConcurrentHashMap.newKeySet(); descriptionSet = ConcurrentHashMap.newKeySet();
} }
public SubjectData getDefaultData() { public LPSubjectData getDefaultData() {
return getDefaults().getSubjectData(); return getDefaults().getSubjectData();
} }
@Override @Override
public Subject getDefaults() { public LPSubject getDefaults() {
return getDefaultSubjects().get("default"); return getDefaultSubjects().get("default");
} }
@Override @Override
public SubjectCollection getSubjects(String s) { public LPSubjectCollection getSubjects(String s) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GET_SUBJECTS)) { try (Timing ignored = plugin.getTimings().time(LPTiming.GET_SUBJECTS)) {
return collections.getUnchecked(s.toLowerCase()); return collections.getUnchecked(s.toLowerCase());
} }
} }
public Map<String, LPSubjectCollection> getCollections() {
return ImmutableMap.copyOf(collections.asMap());
}
@Deprecated
@Override @Override
public Map<String, SubjectCollection> getKnownSubjects() { public Map<String, SubjectCollection> getKnownSubjects() {
return ImmutableMap.copyOf(collections.asMap()); return getCollections().entrySet().stream().collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
} }
@Override @Override
@ -169,36 +191,6 @@ public class LuckPermsService implements PermissionService {
); );
} }
public static ContextSet convertContexts(Set<Context> contexts) {
return ContextSet.fromEntries(contexts.stream().map(c -> Maps.immutableEntry(c.getKey(), c.getValue())).collect(Collectors.toSet()));
}
public static Set<Context> convertContexts(ContextSet contexts) {
return contexts.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet());
}
public static Tristate convertTristate(me.lucko.luckperms.api.Tristate tristate) {
switch (tristate) {
case TRUE:
return Tristate.TRUE;
case FALSE:
return Tristate.FALSE;
default:
return Tristate.UNDEFINED;
}
}
public static me.lucko.luckperms.api.Tristate convertTristate(Tristate tristate) {
switch (tristate) {
case TRUE:
return me.lucko.luckperms.api.Tristate.TRUE;
case FALSE:
return me.lucko.luckperms.api.Tristate.FALSE;
default:
return me.lucko.luckperms.api.Tristate.UNDEFINED;
}
}
@RequiredArgsConstructor @RequiredArgsConstructor
@EqualsAndHashCode @EqualsAndHashCode
@ToString @ToString
@ -241,10 +233,10 @@ public class LuckPermsService implements PermissionService {
service.getDescriptionSet().add(d); service.getDescriptionSet().add(d);
// Set role-templates // Set role-templates
SubjectCollection subjects = service.getSubjects(PermissionService.SUBJECTS_ROLE_TEMPLATE); LPSubjectCollection subjects = service.getSubjects(PermissionService.SUBJECTS_ROLE_TEMPLATE);
for (Map.Entry<String, Tristate> assignment : roles.entrySet()) { for (Map.Entry<String, Tristate> assignment : roles.entrySet()) {
Subject subject = subjects.get(assignment.getKey()); LPSubject subject = subjects.get(assignment.getKey());
subject.getTransientSubjectData().setPermission(SubjectData.GLOBAL_CONTEXT, id, assignment.getValue()); subject.getTransientSubjectData().setPermission(ContextSet.empty(), id, assignment.getValue());
} }
service.getPlugin().getPermissionCache().offer(id); service.getPlugin().getPermissionCache().offer(id);

View File

@ -1,104 +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 com.google.common.collect.ImmutableSet;
import lombok.NonNull;
import me.lucko.luckperms.api.context.ContextSet;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public abstract class LuckPermsSubject implements Subject {
public abstract Tristate getPermissionValue(ContextSet contexts, String permission);
public abstract boolean isChildOf(ContextSet contexts, Subject parent);
public abstract List<Subject> getParents(ContextSet contexts);
public abstract Optional<String> getOption(ContextSet contexts, String s);
public abstract ContextSet getActiveContextSet();
@Override
@Deprecated
public Tristate getPermissionValue(@NonNull Set<Context> contexts, @NonNull String permission) {
return getPermissionValue(LuckPermsService.convertContexts(contexts), permission);
}
@Override
@Deprecated
public boolean hasPermission(@NonNull Set<Context> contexts, @NonNull String permission) {
return getPermissionValue(LuckPermsService.convertContexts(contexts), permission).asBoolean();
}
@Override
@Deprecated
public boolean hasPermission(@NonNull String permission) {
return getPermissionValue(getActiveContextSet(), permission).asBoolean();
}
@Override
@Deprecated
public boolean isChildOf(@NonNull Set<Context> contexts, @NonNull Subject parent) {
return isChildOf(LuckPermsService.convertContexts(contexts), parent);
}
@Override
@Deprecated
public boolean isChildOf(@NonNull Subject parent) {
return isChildOf(getActiveContextSet(), parent);
}
@Override
@Deprecated
public List<Subject> getParents(@NonNull Set<Context> contexts) {
return getParents(LuckPermsService.convertContexts(contexts));
}
@Override
@Deprecated
public List<Subject> getParents() {
return getParents(getActiveContextSet());
}
@Override
@Deprecated
public Optional<String> getOption(@NonNull Set<Context> contexts, @NonNull String s) {
return getOption(LuckPermsService.convertContexts(contexts), s);
}
@Override
@Deprecated
public Optional<String> getOption(@NonNull String key) {
return getOption(getActiveContextSet(), key);
}
@Override
@Deprecated
public Set<Context> getActiveContexts() {
return ImmutableSet.copyOf(LuckPermsService.convertContexts(getActiveContextSet()));
}
}

View File

@ -23,14 +23,15 @@
package me.lucko.luckperms.sponge.service; package me.lucko.luckperms.sponge.service;
import co.aikar.timings.Timing; import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.core.NodeBuilder; import me.lucko.luckperms.common.core.NodeBuilder;
import me.lucko.luckperms.common.core.NodeFactory; import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.Group; import me.lucko.luckperms.common.core.model.Group;
@ -39,18 +40,19 @@ import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException; import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.sponge.model.SpongeGroup; import me.lucko.luckperms.sponge.model.SpongeGroup;
import me.lucko.luckperms.sponge.service.base.LPSubject;
import me.lucko.luckperms.sponge.service.base.LPSubjectData;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming; import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@SuppressWarnings({"OptionalGetWithoutIsPresent", "unused"}) @SuppressWarnings({"OptionalGetWithoutIsPresent", "unused"})
@AllArgsConstructor @AllArgsConstructor
public class LuckPermsSubjectData implements SubjectData { public class LuckPermsSubjectData implements LPSubjectData {
private final boolean enduring; private final boolean enduring;
private final LuckPermsService service; private final LuckPermsService service;
@ -69,12 +71,17 @@ public class LuckPermsSubjectData implements SubjectData {
} }
@Override @Override
public Map<Set<Context>, Map<String, Boolean>> getAllPermissions() { public LPSubject getParentSubject() {
return null;
}
@Override
public Map<ContextSet, Map<String, Boolean>> getPermissions() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) {
Map<Set<Context>, Map<String, Boolean>> perms = new HashMap<>(); Map<ContextSet, Map<String, Boolean>> perms = new HashMap<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) { for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts()); MutableContextSet contexts = MutableContextSet.fromSet(n.getContexts());
if (n.isServerSpecific()) { if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get())); contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
@ -85,41 +92,32 @@ public class LuckPermsSubjectData implements SubjectData {
} }
if (!perms.containsKey(contexts)) { if (!perms.containsKey(contexts)) {
perms.put(contexts, new HashMap<>()); perms.put(contexts.makeImmutable(), new HashMap<>());
} }
perms.get(contexts).put(n.getPermission(), n.getValue()); perms.get(contexts).put(n.getPermission(), n.getValue());
} }
ImmutableMap.Builder<Set<Context>, Map<String, Boolean>> map = ImmutableMap.builder(); ImmutableMap.Builder<ContextSet, Map<String, Boolean>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : perms.entrySet()) { for (Map.Entry<ContextSet, Map<String, Boolean>> e : perms.entrySet()) {
map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue())); map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
} }
return map.build(); return map.build();
} }
} }
@Override @Override
public Map<String, Boolean> getPermissions(@NonNull Set<Context> contexts) { public boolean setPermission(@NonNull ContextSet contexts, @NonNull String permission, @NonNull Tristate tristate) {
return getAllPermissions().getOrDefault(contexts, ImmutableMap.of());
}
@Override
public boolean setPermission(@NonNull Set<Context> contexts, @NonNull String permission, @NonNull Tristate tristate) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_PERMISSION)) { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_PERMISSION)) {
if (tristate == Tristate.UNDEFINED) { if (tristate == Tristate.UNDEFINED) {
// Unset // Unset
Node.Builder builder = new NodeBuilder(permission); Node node = new NodeBuilder(permission).withExtraContext(contexts).build();
for (Context ct : contexts) {
builder.withExtraContext(ct.getKey(), ct.getValue());
}
try { try {
if (enduring) { if (enduring) {
holder.unsetPermission(builder.build()); holder.unsetPermission(node);
} else { } else {
holder.unsetTransientPermission(builder.build()); holder.unsetTransientPermission(node);
} }
} catch (ObjectLacksException ignored) {} } catch (ObjectLacksException ignored) {}
@ -127,14 +125,7 @@ public class LuckPermsSubjectData implements SubjectData {
return true; return true;
} }
Node.Builder builder = new NodeBuilder(permission) Node node = new NodeBuilder(permission).setValue(tristate.asBoolean()).withExtraContext(contexts).build();
.setValue(tristate.asBoolean());
for (Context ct : contexts) {
builder.withExtraContext(ct.getKey(), ct.getValue());
}
Node node = builder.build();
// Workaround: unset the inverse, to allow false -> true, true -> false overrides. // Workaround: unset the inverse, to allow false -> true, true -> false overrides.
try { try {
@ -172,11 +163,11 @@ public class LuckPermsSubjectData implements SubjectData {
} }
@Override @Override
public boolean clearPermissions(@NonNull Set<Context> c) { public boolean clearPermissions(@NonNull ContextSet c) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) {
List<Node> toRemove = new ArrayList<>(); List<Node> toRemove = new ArrayList<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) { for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts()); MutableContextSet contexts = MutableContextSet.fromSet(n.getContexts());
if (n.isServerSpecific()) { if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get())); contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
@ -211,16 +202,16 @@ public class LuckPermsSubjectData implements SubjectData {
} }
@Override @Override
public Map<Set<Context>, List<Subject>> getAllParents() { public Map<ContextSet, Set<SubjectReference>> getParents() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PARENTS)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PARENTS)) {
Map<Set<Context>, List<Subject>> parents = new HashMap<>(); Map<ContextSet, Set<SubjectReference>> parents = new HashMap<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) { for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
if (!n.isGroupNode()) { if (!n.isGroupNode()) {
continue; continue;
} }
Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts()); MutableContextSet contexts = MutableContextSet.fromSet(n.getContexts());
if (n.isServerSpecific()) { if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get())); contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
@ -231,31 +222,25 @@ public class LuckPermsSubjectData implements SubjectData {
} }
if (!parents.containsKey(contexts)) { if (!parents.containsKey(contexts)) {
parents.put(contexts, new ArrayList<>()); parents.put(contexts.makeImmutable(), new HashSet<>());
} }
parents.get(contexts).add(service.getGroupSubjects().get(n.getGroupName())); parents.get(contexts).add(service.getGroupSubjects().get(n.getGroupName()).toReference());
} }
ImmutableMap.Builder<Set<Context>, List<Subject>> map = ImmutableMap.builder(); ImmutableMap.Builder<ContextSet, Set<SubjectReference>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, List<Subject>> e : parents.entrySet()) { for (Map.Entry<ContextSet, Set<SubjectReference>> e : parents.entrySet()) {
map.put(ImmutableSet.copyOf(e.getKey()), ImmutableList.copyOf(e.getValue())); map.put(e.getKey().makeImmutable(), ImmutableSet.copyOf(e.getValue()));
} }
return map.build(); return map.build();
} }
} }
@Override @Override
public List<Subject> getParents(@NonNull Set<Context> contexts) { public boolean addParent(@NonNull ContextSet contexts, @NonNull SubjectReference subject) {
return getAllParents().getOrDefault(contexts, ImmutableList.of());
}
@Override
public boolean addParent(@NonNull Set<Context> set, @NonNull Subject subject) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_ADD_PARENT)) { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_ADD_PARENT)) {
if (subject instanceof SpongeGroup) { if (subject.getCollection().equals(PermissionService.SUBJECTS_GROUP)) {
SpongeGroup permsSubject = ((SpongeGroup) subject); SpongeGroup permsSubject = ((SpongeGroup) subject.resolve(service));
ContextSet contexts = LuckPermsService.convertContexts(set);
try { try {
if (enduring) { if (enduring) {
@ -277,11 +262,10 @@ public class LuckPermsSubjectData implements SubjectData {
} }
@Override @Override
public boolean removeParent(@NonNull Set<Context> set, @NonNull Subject subject) { public boolean removeParent(@NonNull ContextSet contexts, @NonNull SubjectReference subject) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_REMOVE_PARENT)) { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_REMOVE_PARENT)) {
if (subject instanceof SpongeGroup) { if (subject.getCollection().equals(PermissionService.SUBJECTS_GROUP)) {
SpongeGroup permsSubject = ((SpongeGroup) subject); SpongeGroup permsSubject = ((SpongeGroup) subject.resolve(service));
ContextSet contexts = LuckPermsService.convertContexts(set);
try { try {
if (enduring) { if (enduring) {
@ -329,7 +313,7 @@ public class LuckPermsSubjectData implements SubjectData {
} }
@Override @Override
public boolean clearParents(@NonNull Set<Context> set) { public boolean clearParents(@NonNull ContextSet set) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PARENTS)) { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PARENTS)) {
List<Node> toRemove = new ArrayList<>(); List<Node> toRemove = new ArrayList<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) { for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
@ -337,7 +321,7 @@ public class LuckPermsSubjectData implements SubjectData {
continue; continue;
} }
Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts()); MutableContextSet contexts = MutableContextSet.fromSet(n.getContexts());
if (n.isServerSpecific()) { if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get())); contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
@ -372,11 +356,11 @@ public class LuckPermsSubjectData implements SubjectData {
} }
@Override @Override
public Map<Set<Context>, Map<String, String>> getAllOptions() { public Map<ContextSet, Map<String, String>> getOptions() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_OPTIONS)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_OPTIONS)) {
Map<Set<Context>, Map<String, String>> options = new HashMap<>(); Map<ContextSet, Map<String, String>> options = new HashMap<>();
Map<Set<Context>, Integer> minPrefixPriority = new HashMap<>(); Map<ContextSet, Integer> minPrefixPriority = new HashMap<>();
Map<Set<Context>, Integer> minSuffixPriority = new HashMap<>(); Map<ContextSet, Integer> minSuffixPriority = new HashMap<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) { for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
if (!n.getValue()) { if (!n.getValue()) {
@ -387,7 +371,7 @@ public class LuckPermsSubjectData implements SubjectData {
continue; continue;
} }
Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts()); MutableContextSet contexts = MutableContextSet.fromSet(n.getContexts());
if (n.isServerSpecific()) { if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get())); contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
@ -427,24 +411,17 @@ public class LuckPermsSubjectData implements SubjectData {
} }
} }
ImmutableMap.Builder<Set<Context>, Map<String, String>> map = ImmutableMap.builder(); ImmutableMap.Builder<ContextSet, Map<String, String>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, Map<String, String>> e : options.entrySet()) { for (Map.Entry<ContextSet, Map<String, String>> e : options.entrySet()) {
map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue())); map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
} }
return map.build(); return map.build();
} }
} }
@Override @Override
public Map<String, String> getOptions(@NonNull Set<Context> set) { public boolean setOption(@NonNull ContextSet context, @NonNull String key, @NonNull String value) {
return getAllOptions().getOrDefault(set, ImmutableMap.of());
}
@Override
public boolean setOption(@NonNull Set<Context> set, @NonNull String key, @NonNull String value) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_OPTION)) { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_OPTION)) {
ContextSet context = LuckPermsService.convertContexts(set);
List<Node> toRemove = holder.getNodes().stream() List<Node> toRemove = holder.getNodes().stream()
.filter(n -> n.isMeta() && n.getMeta().getKey().equals(key)) .filter(n -> n.isMeta() && n.getMeta().getKey().equals(key))
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -468,7 +445,25 @@ public class LuckPermsSubjectData implements SubjectData {
} }
@Override @Override
public boolean clearOptions(@NonNull Set<Context> set) { public boolean unsetOption(ContextSet contexts, String key) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_OPTION)) {
List<Node> toRemove = holder.getNodes().stream()
.filter(n -> n.isMeta() && n.getMeta().getKey().equals(key))
.collect(Collectors.toList());
toRemove.forEach(n -> {
try {
holder.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
});
objectSave(holder);
return true;
}
}
@Override
public boolean clearOptions(@NonNull ContextSet set) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_OPTIONS)) { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_OPTIONS)) {
List<Node> toRemove = new ArrayList<>(); List<Node> toRemove = new ArrayList<>();
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) { for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
@ -476,7 +471,7 @@ public class LuckPermsSubjectData implements SubjectData {
continue; continue;
} }
Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts()); MutableContextSet contexts = MutableContextSet.fromSet(n.getContexts());
if (n.isServerSpecific()) { if (n.isServerSpecific()) {
contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get())); contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));

View File

@ -0,0 +1,149 @@
/*
* 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.base;
import lombok.NonNull;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static me.lucko.luckperms.sponge.service.base.Util.convertContexts;
import static me.lucko.luckperms.sponge.service.base.Util.convertTristate;
public interface LPSubject extends Subject {
@Override
String getIdentifier();
default Optional<String> getFriendlyIdentifier() {
return Optional.empty();
}
@Override
default Optional<CommandSource> getCommandSource() {
return Optional.empty();
}
SubjectCollectionReference getParentCollection();
LuckPermsService getService();
default SubjectReference toReference() {
return SubjectReference.of(getParentCollection().getCollection(), getIdentifier());
}
@Override
LPSubjectData getSubjectData();
@Override
LPSubjectData getTransientSubjectData();
me.lucko.luckperms.api.Tristate getPermissionValue(ContextSet contexts, String permission);
boolean isChildOf(ContextSet contexts, SubjectReference parent);
Set<SubjectReference> getParents(ContextSet contexts);
Optional<String> getOption(ContextSet contexts, String key);
ContextSet getActiveContextSet();
/* Compat */
@Deprecated
@Override
default LPSubjectCollection getContainingCollection() {
return getParentCollection().resolve(getService());
}
@Deprecated
@Override
default boolean hasPermission(@NonNull Set<Context> contexts, @NonNull String permission) {
return getPermissionValue(convertContexts(contexts), permission).asBoolean();
}
@Deprecated
@Override
default boolean hasPermission(@NonNull String permission) {
return getPermissionValue(getActiveContextSet(), permission).asBoolean();
}
@Deprecated
@Override
default Tristate getPermissionValue(@NonNull Set<Context> contexts, @NonNull String permission) {
return convertTristate(getPermissionValue(convertContexts(contexts), permission));
}
@Deprecated
@Override
default boolean isChildOf(@NonNull Subject parent) {
return isChildOf(getActiveContextSet(), SubjectReference.of(parent));
}
@Deprecated
@Override
default boolean isChildOf(@NonNull Set<Context> contexts, @NonNull Subject parent) {
return isChildOf(convertContexts(contexts), SubjectReference.of(parent));
}
@Deprecated
@Override
default List<Subject> getParents() {
return getParents(getActiveContextSet()).stream().map(s -> s.resolve(getService())).collect(ImmutableCollectors.toImmutableList());
}
@Deprecated
@Override
default List<Subject> getParents(@NonNull Set<Context> contexts) {
return getParents(convertContexts(contexts)).stream().map(s -> s.resolve(getService())).collect(ImmutableCollectors.toImmutableList());
}
@Deprecated
@Override
default Optional<String> getOption(@NonNull String key) {
return getOption(getActiveContextSet(), key);
}
@Deprecated
@Override
default Optional<String> getOption(@NonNull Set<Context> contexts, @NonNull String key) {
return getOption(getActiveContextSet(), key);
}
@Deprecated
@Override
default Set<Context> getActiveContexts() {
return convertContexts(getActiveContextSet());
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.base;
import lombok.NonNull;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import static me.lucko.luckperms.sponge.service.base.Util.convertContexts;
public interface LPSubjectCollection extends SubjectCollection {
@Override
String getIdentifier();
LuckPermsService getService();
default SubjectCollectionReference toReference() {
return SubjectCollectionReference.of(getIdentifier());
}
@Override
LPSubject get(String identifier);
@Override
boolean hasRegistered(String identifier);
Collection<LPSubject> getSubjects();
default Map<LPSubject, Boolean> getWithPermission(String permission) {
return getWithPermission(ContextSet.empty(), permission);
}
Map<LPSubject, Boolean> getWithPermission(ContextSet contexts, String permission);
SubjectReference getDefaultSubject();
@Deprecated
@Override
default Subject getDefaults() {
return getDefaultSubject().resolve(getService());
}
@Deprecated
@Override
default Iterable<Subject> getAllSubjects() {
return getSubjects().stream().collect(ImmutableCollectors.toImmutableList());
}
@Deprecated
@Override
default Map<Subject, Boolean> getAllWithPermission(@NonNull String permission) {
return getWithPermission(permission).entrySet().stream().collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}
@Deprecated
@Override
default Map<Subject, Boolean> getAllWithPermission(@NonNull Set<Context> contexts, @NonNull String permission) {
return getWithPermission(convertContexts(contexts), permission).entrySet().stream().collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}
}

View File

@ -0,0 +1,187 @@
/*
* 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.base;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static me.lucko.luckperms.sponge.service.base.Util.convertContexts;
import static me.lucko.luckperms.sponge.service.base.Util.convertTristate;
public interface LPSubjectData extends SubjectData {
LPSubject getParentSubject();
Map<ContextSet, Map<String, Boolean>> getPermissions();
default Map<String, Boolean> getPermissions(ContextSet contexts) {
return ImmutableMap.copyOf(getPermissions().getOrDefault(contexts, ImmutableMap.of()));
}
boolean setPermission(ContextSet contexts, String permission, me.lucko.luckperms.api.Tristate value);
@Override
boolean clearPermissions();
boolean clearPermissions(ContextSet contexts);
Map<ContextSet, Set<SubjectReference>> getParents();
default Set<SubjectReference> getParents(ContextSet contexts) {
return ImmutableSet.copyOf(getParents().getOrDefault(contexts, ImmutableSet.of()));
}
boolean addParent(ContextSet contexts, SubjectReference parent);
boolean removeParent(ContextSet contexts, SubjectReference parent);
@Override
boolean clearParents();
boolean clearParents(ContextSet contexts);
Map<ContextSet, Map<String, String>> getOptions();
default Map<String, String> getOptions(ContextSet contexts) {
return ImmutableMap.copyOf(getOptions().getOrDefault(contexts, ImmutableMap.of()));
}
boolean setOption(ContextSet contexts, String key, String value);
boolean unsetOption(ContextSet contexts, String key);
boolean clearOptions(ContextSet contexts);
@Override
boolean clearOptions();
/* Compat */
@Deprecated
@Override
default Map<Set<Context>, Map<String, Boolean>> getAllPermissions() {
return getPermissions().entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
e -> convertContexts(e.getKey()),
e -> ImmutableMap.copyOf(e.getValue()))
);
}
@Deprecated
@Override
default Map<String, Boolean> getPermissions(Set<Context> contexts) {
return ImmutableMap.copyOf(getPermissions(convertContexts(contexts)));
}
@Deprecated
@Override
default boolean setPermission(Set<Context> contexts, String permission, Tristate value) {
return setPermission(convertContexts(contexts), permission, convertTristate(value));
}
@Deprecated
@Override
default boolean clearPermissions(Set<Context> contexts) {
return clearPermissions(convertContexts(contexts));
}
@Deprecated
@Override
default Map<Set<Context>, List<Subject>> getAllParents() {
return getParents().entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
e -> convertContexts(e.getKey()),
e -> e.getValue().stream()
.map(s -> s.resolve(getParentSubject().getService()))
.collect(ImmutableCollectors.toImmutableList())
)
);
}
@Deprecated
@Override
default List<Subject> getParents(Set<Context> contexts) {
return getParents(convertContexts(contexts)).stream().map(s -> s.resolve(getParentSubject().getService())).collect(ImmutableCollectors.toImmutableList());
}
@Deprecated
@Override
default boolean addParent(Set<Context> contexts, Subject parent) {
return addParent(convertContexts(contexts), SubjectReference.of(parent));
}
@Deprecated
@Override
default boolean removeParent(Set<Context> contexts, Subject parent) {
return removeParent(convertContexts(contexts), SubjectReference.of(parent));
}
@Deprecated
@Override
default boolean clearParents(Set<Context> contexts) {
return clearParents(convertContexts(contexts));
}
@Deprecated
@Override
default Map<Set<Context>, Map<String, String>> getAllOptions() {
return getOptions().entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
e -> convertContexts(e.getKey()),
e -> ImmutableMap.copyOf(e.getValue()))
);
}
@Deprecated
@Override
default Map<String, String> getOptions(Set<Context> contexts) {
return ImmutableMap.copyOf(getOptions(convertContexts(contexts)));
}
@Deprecated
@Override
default boolean setOption(Set<Context> contexts, String key, @Nullable String value) {
return value == null ? unsetOption(convertContexts(contexts), key) : setOption(convertContexts(contexts), key, value);
}
@Deprecated
@Override
default boolean clearOptions(Set<Context> contexts) {
return clearOptions(convertContexts(contexts));
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.base;
import com.google.common.collect.Maps;
import lombok.experimental.UtilityClass;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.util.Tristate;
import java.util.Set;
import java.util.stream.Collectors;
@UtilityClass
public class Util {
public static ContextSet convertContexts(Set<Context> contexts) {
return ContextSet.fromEntries(contexts.stream().map(c -> Maps.immutableEntry(c.getKey(), c.getValue())).collect(Collectors.toSet()));
}
public static Set<Context> convertContexts(ContextSet contexts) {
return contexts.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(ImmutableCollectors.toImmutableSet());
}
public static Tristate convertTristate(me.lucko.luckperms.api.Tristate tristate) {
switch (tristate) {
case TRUE:
return Tristate.TRUE;
case FALSE:
return Tristate.FALSE;
default:
return Tristate.UNDEFINED;
}
}
public static me.lucko.luckperms.api.Tristate convertTristate(Tristate tristate) {
switch (tristate) {
case TRUE:
return me.lucko.luckperms.api.Tristate.TRUE;
case FALSE:
return me.lucko.luckperms.api.Tristate.FALSE;
default:
return me.lucko.luckperms.api.Tristate.UNDEFINED;
}
}
}

View File

@ -20,7 +20,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package me.lucko.luckperms.sponge.service.data; package me.lucko.luckperms.sponge.service.calculated;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
@ -31,32 +31,34 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.calculators.PermissionCalculator; import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.calculators.PermissionProcessor; import me.lucko.luckperms.common.calculators.PermissionProcessor;
import me.lucko.luckperms.common.calculators.processors.MapProcessor; import me.lucko.luckperms.common.calculators.processors.MapProcessor;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.calculators.SpongeWildcardProcessor; import me.lucko.luckperms.sponge.calculators.SpongeWildcardProcessor;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import org.spongepowered.api.service.context.Context; import me.lucko.luckperms.sponge.service.base.LPSubject;
import org.spongepowered.api.service.permission.Subject; import me.lucko.luckperms.sponge.service.base.LPSubjectData;
import org.spongepowered.api.service.permission.SubjectData; import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.util.Tristate;
import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@RequiredArgsConstructor @RequiredArgsConstructor
public class CalculatedSubjectData implements SubjectData { public class CalculatedSubjectData implements LPSubjectData {
private static final ContextComparator CONTEXT_COMPARATOR = new ContextComparator(); private static final ContextComparator CONTEXT_COMPARATOR = new ContextComparator();
@Getter
private final LPSubject parentSubject;
private final LuckPermsService service; private final LuckPermsService service;
private final String calculatorDisplayName; private final String calculatorDisplayName;
private final LoadingCache<Set<Context>, CalculatorHolder> permissionCache = CacheBuilder.newBuilder() private final LoadingCache<ContextSet, CalculatorHolder> permissionCache = CacheBuilder.newBuilder()
.build(new CacheLoader<Set<Context>, CalculatorHolder>() { .build(new CacheLoader<ContextSet, CalculatorHolder>() {
@Override @Override
public CalculatorHolder load(Set<Context> contexts) { public CalculatorHolder load(ContextSet contexts) {
ImmutableList.Builder<PermissionProcessor> processors = ImmutableList.builder(); ImmutableList.Builder<PermissionProcessor> processors = ImmutableList.builder();
processors.add(new MapProcessor()); processors.add(new MapProcessor());
processors.add(new SpongeWildcardProcessor()); processors.add(new SpongeWildcardProcessor());
@ -68,77 +70,69 @@ public class CalculatedSubjectData implements SubjectData {
} }
}); });
private final LoadingCache<Set<Context>, Map<String, String>> optionCache = CacheBuilder.newBuilder() private final LoadingCache<ContextSet, Map<String, String>> optionCache = CacheBuilder.newBuilder()
.build(new CacheLoader<Set<Context>, Map<String, String>>() { .build(new CacheLoader<ContextSet, Map<String, String>>() {
@Override @Override
public Map<String, String> load(Set<Context> contexts) { public Map<String, String> load(ContextSet contexts) {
return flattenMap(contexts, options); return flattenMap(contexts, options);
} }
}); });
private final Map<Set<Context>, Map<String, Boolean>> permissions = new ConcurrentHashMap<>(); private final Map<ContextSet, Map<String, Boolean>> permissions = new ConcurrentHashMap<>();
private final Map<Set<Context>, Set<SubjectReference>> parents = new ConcurrentHashMap<>(); private final Map<ContextSet, Set<SubjectReference>> parents = new ConcurrentHashMap<>();
private final Map<Set<Context>, Map<String, String>> options = new ConcurrentHashMap<>(); private final Map<ContextSet, Map<String, String>> options = new ConcurrentHashMap<>();
public Tristate getPermissionValue(Set<Context> contexts, String permission) { public Tristate getPermissionValue(ContextSet contexts, String permission) {
return LuckPermsService.convertTristate(permissionCache.getUnchecked(contexts).getCalculator().getPermissionValue(permission)); return permissionCache.getUnchecked(contexts).getCalculator().getPermissionValue(permission);
} }
public Map<Set<Context>, Set<SubjectReference>> getParents() { public void replacePermissions(Map<ContextSet, Map<String, Boolean>> map) {
ImmutableMap.Builder<Set<Context>, Set<SubjectReference>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, Set<SubjectReference>> e : parents.entrySet()) {
map.put(ImmutableSet.copyOf(e.getKey()), ImmutableSet.copyOf(e.getValue()));
}
return map.build();
}
public void replacePermissions(Map<Set<Context>, Map<String, Boolean>> map) {
permissions.clear(); permissions.clear();
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : map.entrySet()) { for (Map.Entry<ContextSet, Map<String, Boolean>> e : map.entrySet()) {
permissions.put(ImmutableSet.copyOf(e.getKey()), new ConcurrentHashMap<>(e.getValue())); permissions.put(e.getKey().makeImmutable(), new ConcurrentHashMap<>(e.getValue()));
} }
permissionCache.invalidateAll(); permissionCache.invalidateAll();
} }
public void replaceParents(Map<Set<Context>, Set<SubjectReference>> map) { public void replaceParents(Map<ContextSet, Set<SubjectReference>> map) {
parents.clear(); parents.clear();
for (Map.Entry<Set<Context>, Set<SubjectReference>> e : map.entrySet()) { for (Map.Entry<ContextSet, Set<SubjectReference>> e : map.entrySet()) {
Set<SubjectReference> set = ConcurrentHashMap.newKeySet(); Set<SubjectReference> set = ConcurrentHashMap.newKeySet();
set.addAll(e.getValue()); set.addAll(e.getValue());
parents.put(ImmutableSet.copyOf(e.getKey()), set); parents.put(e.getKey().makeImmutable(), set);
} }
} }
public void replaceOptions(Map<Set<Context>, Map<String, String>> map) { public void replaceOptions(Map<ContextSet, Map<String, String>> map) {
options.clear(); options.clear();
for (Map.Entry<Set<Context>, Map<String, String>> e : map.entrySet()) { for (Map.Entry<ContextSet, Map<String, String>> e : map.entrySet()) {
options.put(ImmutableSet.copyOf(e.getKey()), new ConcurrentHashMap<>(e.getValue())); options.put(e.getKey().makeImmutable(), new ConcurrentHashMap<>(e.getValue()));
} }
optionCache.invalidateAll(); optionCache.invalidateAll();
} }
@Override @Override
public Map<Set<Context>, Map<String, Boolean>> getAllPermissions() { public Map<ContextSet, Map<String, Boolean>> getPermissions() {
ImmutableMap.Builder<Set<Context>, Map<String, Boolean>> map = ImmutableMap.builder(); ImmutableMap.Builder<ContextSet, Map<String, Boolean>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : permissions.entrySet()) { for (Map.Entry<ContextSet, Map<String, Boolean>> e : permissions.entrySet()) {
map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue())); map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
} }
return map.build(); return map.build();
} }
@Override @Override
public Map<String, Boolean> getPermissions(Set<Context> contexts) { public Map<String, Boolean> getPermissions(ContextSet contexts) {
return ImmutableMap.copyOf(permissions.getOrDefault(contexts, ImmutableMap.of())); return ImmutableMap.copyOf(permissions.getOrDefault(contexts, ImmutableMap.of()));
} }
@Override @Override
public boolean setPermission(Set<Context> contexts, String permission, Tristate value) { public boolean setPermission(ContextSet contexts, String permission, Tristate value) {
boolean b; boolean b;
if (value == Tristate.UNDEFINED) { if (value == Tristate.UNDEFINED) {
Map<String, Boolean> perms = permissions.get(contexts); Map<String, Boolean> perms = permissions.get(contexts);
b = perms != null && perms.remove(permission.toLowerCase()) != null; b = perms != null && perms.remove(permission.toLowerCase()) != null;
} else { } else {
Map<String, Boolean> perms = permissions.computeIfAbsent(ImmutableSet.copyOf(contexts), c -> new ConcurrentHashMap<>()); Map<String, Boolean> perms = permissions.computeIfAbsent(contexts.makeImmutable(), c -> new ConcurrentHashMap<>());
b = !Objects.equals(perms.put(permission.toLowerCase(), value.asBoolean()), value.asBoolean()); b = !Objects.equals(perms.put(permission.toLowerCase(), value.asBoolean()), value.asBoolean());
} }
if (b) { if (b) {
@ -159,7 +153,7 @@ public class CalculatedSubjectData implements SubjectData {
} }
@Override @Override
public boolean clearPermissions(Set<Context> contexts) { public boolean clearPermissions(ContextSet contexts) {
Map<String, Boolean> perms = permissions.get(contexts); Map<String, Boolean> perms = permissions.get(contexts);
if (perms == null) { if (perms == null) {
return false; return false;
@ -174,34 +168,29 @@ public class CalculatedSubjectData implements SubjectData {
} }
@Override @Override
public Map<Set<Context>, List<Subject>> getAllParents() { public Map<ContextSet, Set<SubjectReference>> getParents() {
ImmutableMap.Builder<Set<Context>, List<Subject>> map = ImmutableMap.builder(); ImmutableMap.Builder<ContextSet, Set<SubjectReference>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, Set<SubjectReference>> e : parents.entrySet()) { for (Map.Entry<ContextSet, Set<SubjectReference>> e : parents.entrySet()) {
map.put( map.put(e.getKey().makeImmutable(), ImmutableSet.copyOf(e.getValue()));
ImmutableSet.copyOf(e.getKey()),
e.getValue().stream().map(s -> s.resolve(service)).collect(ImmutableCollectors.toImmutableList())
);
} }
return map.build(); return map.build();
} }
@Override @Override
public List<Subject> getParents(Set<Context> contexts) { public Set<SubjectReference> getParents(ContextSet contexts) {
return parents.getOrDefault(contexts, ImmutableSet.of()).stream() return ImmutableSet.copyOf(parents.getOrDefault(contexts, ImmutableSet.of()));
.map(s -> s.resolve(service))
.collect(ImmutableCollectors.toImmutableList());
} }
@Override @Override
public boolean addParent(Set<Context> contexts, Subject parent) { public boolean addParent(ContextSet contexts, SubjectReference parent) {
Set<SubjectReference> set = parents.computeIfAbsent(ImmutableSet.copyOf(contexts), c -> ConcurrentHashMap.newKeySet()); Set<SubjectReference> set = parents.computeIfAbsent(contexts.makeImmutable(), c -> ConcurrentHashMap.newKeySet());
return set.add(SubjectReference.of(parent)); return set.add(parent);
} }
@Override @Override
public boolean removeParent(Set<Context> contexts, Subject parent) { public boolean removeParent(ContextSet contexts, SubjectReference parent) {
Set<SubjectReference> set = parents.get(contexts); Set<SubjectReference> set = parents.get(contexts);
return set != null && set.remove(SubjectReference.of(parent)); return set != null && set.remove(parent);
} }
@Override @Override
@ -215,7 +204,7 @@ public class CalculatedSubjectData implements SubjectData {
} }
@Override @Override
public boolean clearParents(Set<Context> contexts) { public boolean clearParents(ContextSet contexts) {
Set<SubjectReference> set = parents.get(contexts); Set<SubjectReference> set = parents.get(contexts);
if (set == null) { if (set == null) {
return false; return false;
@ -226,29 +215,33 @@ public class CalculatedSubjectData implements SubjectData {
} }
@Override @Override
public Map<Set<Context>, Map<String, String>> getAllOptions() { public Map<ContextSet, Map<String, String>> getOptions() {
ImmutableMap.Builder<Set<Context>, Map<String, String>> map = ImmutableMap.builder(); ImmutableMap.Builder<ContextSet, Map<String, String>> map = ImmutableMap.builder();
for (Map.Entry<Set<Context>, Map<String, String>> e : options.entrySet()) { for (Map.Entry<ContextSet, Map<String, String>> e : options.entrySet()) {
map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue())); map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
} }
return map.build(); return map.build();
} }
@Override @Override
public Map<String, String> getOptions(Set<Context> contexts) { public Map<String, String> getOptions(ContextSet contexts) {
return ImmutableMap.copyOf(options.getOrDefault(contexts, ImmutableMap.of())); return ImmutableMap.copyOf(options.getOrDefault(contexts, ImmutableMap.of()));
} }
@Override @Override
public boolean setOption(Set<Context> contexts, String key, @Nullable String value) { public boolean setOption(ContextSet contexts, String key, String value) {
boolean b; Map<String, String> options = this.options.computeIfAbsent(contexts.makeImmutable(), c -> new ConcurrentHashMap<>());
if (value == null) { boolean b = !stringEquals(options.put(key.toLowerCase(), value), value);
Map<String, String> options = this.options.get(contexts); if (b) {
b = options != null && options.remove(key.toLowerCase()) != null; optionCache.invalidateAll();
} else {
Map<String, String> options = this.options.computeIfAbsent(ImmutableSet.copyOf(contexts), c -> new ConcurrentHashMap<>());
b = !stringEquals(options.put(key.toLowerCase(), value), value);
} }
return b;
}
@Override
public boolean unsetOption(ContextSet contexts, String key) {
Map<String, String> options = this.options.get(contexts);
boolean b = options != null && options.remove(key.toLowerCase()) != null;
if (b) { if (b) {
optionCache.invalidateAll(); optionCache.invalidateAll();
} }
@ -267,7 +260,7 @@ public class CalculatedSubjectData implements SubjectData {
} }
@Override @Override
public boolean clearOptions(Set<Context> contexts) { public boolean clearOptions(ContextSet contexts) {
Map<String, String> map = options.get(contexts); Map<String, String> map = options.get(contexts);
if (map == null) { if (map == null) {
return false; return false;
@ -281,10 +274,10 @@ public class CalculatedSubjectData implements SubjectData {
return false; return false;
} }
private static <V> Map<String, V> flattenMap(Set<Context> contexts, Map<Set<Context>, Map<String, V>> source) { private static <V> Map<String, V> flattenMap(ContextSet contexts, Map<ContextSet, Map<String, V>> source) {
Map<String, V> map = new HashMap<>(); Map<String, V> map = new HashMap<>();
SortedMap<Set<Context>, Map<String, V>> ret = getRelevantEntries(contexts, source); SortedMap<ContextSet, Map<String, V>> ret = getRelevantEntries(contexts, source);
for (Map<String, V> m : ret.values()) { for (Map<String, V> m : ret.values()) {
for (Map.Entry<String, V> e : m.entrySet()) { for (Map.Entry<String, V> e : m.entrySet()) {
if (!map.containsKey(e.getKey())) { if (!map.containsKey(e.getKey())) {
@ -296,18 +289,19 @@ public class CalculatedSubjectData implements SubjectData {
return ImmutableMap.copyOf(map); return ImmutableMap.copyOf(map);
} }
private static <K, V> SortedMap<Set<Context>, Map<K, V>> getRelevantEntries(Set<Context> set, Map<Set<Context>, Map<K, V>> map) { private static <K, V> SortedMap<ContextSet, Map<K, V>> getRelevantEntries(ContextSet set, Map<ContextSet, Map<K, V>> map) {
ImmutableSortedMap.Builder<Set<Context>, Map<K, V>> perms = ImmutableSortedMap.orderedBy(CONTEXT_COMPARATOR); ImmutableSortedMap.Builder<ContextSet, Map<K, V>> perms = ImmutableSortedMap.orderedBy(CONTEXT_COMPARATOR);
loop: loop:
for (Map.Entry<Set<Context>, Map<K, V>> e : map.entrySet()) { for (Map.Entry<ContextSet, Map<K, V>> e : map.entrySet()) {
for (Context c : e.getKey()) {
if (!set.contains(c)) { for (Map.Entry<String, String> c : e.getKey().toSet()) {
if (!set.has(c.getKey(), c.getValue())) {
continue loop; continue loop;
} }
} }
perms.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue())); perms.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
} }
return perms.build(); return perms.build();
@ -317,10 +311,10 @@ public class CalculatedSubjectData implements SubjectData {
return a == null && b == null || a != null && b != null && a.equalsIgnoreCase(b); return a == null && b == null || a != null && b != null && a.equalsIgnoreCase(b);
} }
private static class ContextComparator implements Comparator<Set<Context>> { private static class ContextComparator implements Comparator<ContextSet> {
@Override @Override
public int compare(Set<Context> o1, Set<Context> o2) { public int compare(ContextSet o1, ContextSet o2) {
int i = Integer.compare(o1.size(), o2.size()); int i = Integer.compare(o1.size(), o2.size());
return i == 0 ? 1 : i; return i == 0 ? 1 : i;
} }

View File

@ -0,0 +1,40 @@
/*
* 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.calculated;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import me.lucko.luckperms.api.context.ContextSet;
@Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor(staticName = "of")
public class OptionLookup {
private final String key;
private final ContextSet contexts;
}

View File

@ -0,0 +1,40 @@
/*
* 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.calculated;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import me.lucko.luckperms.api.context.ContextSet;
@Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor(staticName = "of")
public class PermissionLookup {
private final String node;
private final ContextSet contexts;
}

View File

@ -26,30 +26,31 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.utils.ImmutableCollectors; import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import org.spongepowered.api.service.context.Context; import me.lucko.luckperms.sponge.service.base.LPSubject;
import org.spongepowered.api.service.permission.Subject; import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
import org.spongepowered.api.service.permission.SubjectCollection; import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.util.Tristate;
import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* A simple persistable subject collection * A simple persistable subject collection
*/ */
@RequiredArgsConstructor
public class PersistedCollection implements SubjectCollection {
private final LuckPermsService service;
@Getter @Getter
@RequiredArgsConstructor
public class PersistedCollection implements LPSubjectCollection {
private final LuckPermsService service;
private final String identifier; private final String identifier;
@Getter(AccessLevel.NONE)
private final LoadingCache<String, PersistedSubject> subjects = CacheBuilder.newBuilder() private final LoadingCache<String, PersistedSubject> subjects = CacheBuilder.newBuilder()
.build(new CacheLoader<String, PersistedSubject>() { .build(new CacheLoader<String, PersistedSubject>() {
@Override @Override
@ -77,19 +78,14 @@ public class PersistedCollection implements SubjectCollection {
} }
@Override @Override
public Iterable<Subject> getAllSubjects() { public Collection<LPSubject> getSubjects() {
return subjects.asMap().values().stream().map(s -> (Subject) s).collect(ImmutableCollectors.toImmutableList()); return subjects.asMap().values().stream().map(s -> (LPSubject) s).collect(ImmutableCollectors.toImmutableList());
} }
@Override @Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull String id) { public Map<LPSubject, Boolean> getWithPermission(@NonNull ContextSet contexts, @NonNull String node) {
return getAllWithPermission(ImmutableSet.of(), id); ImmutableMap.Builder<LPSubject, Boolean> m = ImmutableMap.builder();
} for (LPSubject subject : subjects.asMap().values()) {
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull Set<Context> contexts, @NonNull String node) {
ImmutableMap.Builder<Subject, Boolean> m = ImmutableMap.builder();
for (Subject subject : subjects.asMap().values()) {
Tristate ts = subject.getPermissionValue(contexts, node); Tristate ts = subject.getPermissionValue(contexts, node);
if (ts != Tristate.UNDEFINED) { if (ts != Tristate.UNDEFINED) {
m.put(subject, ts.asBoolean()); m.put(subject, ts.asBoolean());
@ -100,7 +96,7 @@ public class PersistedCollection implements SubjectCollection {
} }
@Override @Override
public Subject getDefaults() { public SubjectReference getDefaultSubject() {
return service.getDefaultSubjects().get(identifier); return SubjectReference.of("defaults", identifier);
} }
} }

View File

@ -23,22 +23,22 @@
package me.lucko.luckperms.sponge.service.persisted; package me.lucko.luckperms.sponge.service.persisted;
import co.aikar.timings.Timing; import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.utils.BufferedRequest; import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.data.CalculatedSubjectData; import me.lucko.luckperms.sponge.service.base.LPSubject;
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming; import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.util.Tristate;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.HashSet;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -46,11 +46,12 @@ import java.util.Set;
* A simple persistable Subject implementation * A simple persistable Subject implementation
*/ */
@Getter @Getter
public class PersistedSubject implements Subject { public class PersistedSubject implements LPSubject {
private final String identifier; private final String identifier;
@Getter
private final LuckPermsService service; private final LuckPermsService service;
private final PersistedCollection containingCollection; private final SubjectCollectionReference parentCollection;
private final PersistedSubjectData subjectData; private final PersistedSubjectData subjectData;
private final CalculatedSubjectData transientSubjectData; private final CalculatedSubjectData transientSubjectData;
private final BufferedRequest<Void> saveBuffer = new BufferedRequest<Void>(1000L, r -> PersistedSubject.this.service.getPlugin().doAsync(r)) { private final BufferedRequest<Void> saveBuffer = new BufferedRequest<Void>(1000L, r -> PersistedSubject.this.service.getPlugin().doAsync(r)) {
@ -70,9 +71,9 @@ public class PersistedSubject implements Subject {
public PersistedSubject(String identifier, LuckPermsService service, PersistedCollection containingCollection) { public PersistedSubject(String identifier, LuckPermsService service, PersistedCollection containingCollection) {
this.identifier = identifier; this.identifier = identifier;
this.service = service; this.service = service;
this.containingCollection = containingCollection; this.parentCollection = containingCollection.toReference();
this.subjectData = new PersistedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(p)", this); this.subjectData = new PersistedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(p)", this);
this.transientSubjectData = new CalculatedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(t)"); this.transientSubjectData = new CalculatedSubjectData(this, service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(t)");
} }
public void loadData(SubjectDataHolder dataHolder) { public void loadData(SubjectDataHolder dataHolder) {
@ -91,12 +92,7 @@ public class PersistedSubject implements Subject {
} }
@Override @Override
public boolean hasPermission(@NonNull Set<Context> contexts, @NonNull String node) { public Tristate getPermissionValue(@NonNull ContextSet contexts, @NonNull String node) {
return getPermissionValue(contexts, node).asBoolean();
}
@Override
public Tristate getPermissionValue(@NonNull Set<Context> contexts, @NonNull String node) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_PERMISSION_VALUE)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_PERMISSION_VALUE)) {
Tristate res = subjectData.getPermissionValue(contexts, node); Tristate res = subjectData.getPermissionValue(contexts, node);
if (res != Tristate.UNDEFINED) { if (res != Tristate.UNDEFINED) {
@ -108,18 +104,18 @@ public class PersistedSubject implements Subject {
return res; return res;
} }
for (Subject parent : getParents(contexts)) { for (SubjectReference parent : getParents(contexts)) {
Tristate tempRes = parent.getPermissionValue(contexts, node); Tristate tempRes = parent.resolve(service).getPermissionValue(contexts, node);
if (tempRes != Tristate.UNDEFINED) { if (tempRes != Tristate.UNDEFINED) {
return tempRes; return tempRes;
} }
} }
if (getContainingCollection().getIdentifier().equalsIgnoreCase("defaults")) { if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
return Tristate.UNDEFINED; return Tristate.UNDEFINED;
} }
res = service.getGroupSubjects().getDefaults().getPermissionValue(contexts, node); res = service.getGroupSubjects().getDefaultSubject().resolve(service).getPermissionValue(contexts, node);
if (res != Tristate.UNDEFINED) { if (res != Tristate.UNDEFINED) {
return res; return res;
} }
@ -130,61 +126,61 @@ public class PersistedSubject implements Subject {
} }
@Override @Override
public boolean isChildOf(@NonNull Set<Context> contexts, @NonNull Subject subject) { public boolean isChildOf(@NonNull ContextSet contexts, @NonNull SubjectReference subject) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_IS_CHILD_OF)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_IS_CHILD_OF)) {
if (getContainingCollection().getIdentifier().equalsIgnoreCase("defaults")) { if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
return subjectData.getParents(contexts).contains(subject) || return subjectData.getParents(contexts).contains(subject) ||
transientSubjectData.getParents(contexts).contains(subject); transientSubjectData.getParents(contexts).contains(subject);
} else { } else {
return subjectData.getParents(contexts).contains(subject) || return subjectData.getParents(contexts).contains(subject) ||
transientSubjectData.getParents(contexts).contains(subject) || transientSubjectData.getParents(contexts).contains(subject) ||
getContainingCollection().getDefaults().getParents(contexts).contains(subject) || getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts).contains(subject) ||
service.getDefaults().getParents(contexts).contains(subject); service.getDefaults().getParents(contexts).contains(subject);
} }
} }
} }
@Override @Override
public List<Subject> getParents(@NonNull Set<Context> contexts) { public Set<SubjectReference> getParents(@NonNull ContextSet contexts) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_PARENTS)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_PARENTS)) {
List<Subject> s = new ArrayList<>(); Set<SubjectReference> s = new HashSet<>();
s.addAll(subjectData.getParents(contexts)); s.addAll(subjectData.getParents(contexts));
s.addAll(transientSubjectData.getParents(contexts)); s.addAll(transientSubjectData.getParents(contexts));
if (!getContainingCollection().getIdentifier().equalsIgnoreCase("defaults")) { if (!getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
s.addAll(getContainingCollection().getDefaults().getParents(contexts)); s.addAll(getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts));
s.addAll(service.getDefaults().getParents(contexts)); s.addAll(service.getDefaults().getParents(contexts));
} }
return ImmutableList.copyOf(s); return ImmutableSet.copyOf(s);
} }
} }
@Override @Override
public Optional<String> getOption(Set<Context> set, String key) { public Optional<String> getOption(ContextSet set, String key) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_OPTION)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_OPTION)) {
Optional<String> res = Optional.ofNullable(subjectData.getOptions(getActiveContexts()).get(key)); Optional<String> res = Optional.ofNullable(subjectData.getOptions(getActiveContextSet()).get(key));
if (res.isPresent()) { if (res.isPresent()) {
return res; return res;
} }
res = Optional.ofNullable(transientSubjectData.getOptions(getActiveContexts()).get(key)); res = Optional.ofNullable(transientSubjectData.getOptions(getActiveContextSet()).get(key));
if (res.isPresent()) { if (res.isPresent()) {
return res; return res;
} }
for (Subject parent : getParents(getActiveContexts())) { for (SubjectReference parent : getParents(getActiveContextSet())) {
Optional<String> tempRes = parent.getOption(getActiveContexts(), key); Optional<String> tempRes = parent.resolve(service).getOption(getActiveContextSet(), key);
if (tempRes.isPresent()) { if (tempRes.isPresent()) {
return tempRes; return tempRes;
} }
} }
if (getContainingCollection().getIdentifier().equalsIgnoreCase("defaults")) { if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
return Optional.empty(); return Optional.empty();
} }
res = getContainingCollection().getDefaults().getOption(set, key); res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getOption(set, key);
if (res.isPresent()) { if (res.isPresent()) {
return res; return res;
} }
@ -194,9 +190,9 @@ public class PersistedSubject implements Subject {
} }
@Override @Override
public Set<Context> getActiveContexts() { public ContextSet getActiveContextSet() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_ACTIVE_CONTEXTS)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_ACTIVE_CONTEXTS)) {
return ImmutableSet.copyOf(LuckPermsService.convertContexts(service.getPlugin().getContextManager().getApplicableContext(this))); return service.getPlugin().getContextManager().getApplicableContext(this);
} }
} }
} }

View File

@ -24,14 +24,10 @@ package me.lucko.luckperms.sponge.service.persisted;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.data.CalculatedSubjectData; import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
import org.spongepowered.api.service.context.Context; import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.util.Tristate;
import javax.annotation.Nullable;
import java.util.Set;
/** /**
* Extension of MemorySubjectData which persists data when modified * Extension of MemorySubjectData which persists data when modified
@ -44,7 +40,7 @@ public class PersistedSubjectData extends CalculatedSubjectData {
private boolean save = true; private boolean save = true;
public PersistedSubjectData(LuckPermsService service, String calculatorDisplayName, PersistedSubject subject) { public PersistedSubjectData(LuckPermsService service, String calculatorDisplayName, PersistedSubject subject) {
super(service, calculatorDisplayName); super(subject, service, calculatorDisplayName);
this.subject = subject; this.subject = subject;
} }
@ -59,7 +55,7 @@ public class PersistedSubjectData extends CalculatedSubjectData {
} }
@Override @Override
public boolean setPermission(Set<Context> contexts, String permission, Tristate value) { public boolean setPermission(ContextSet contexts, String permission, me.lucko.luckperms.api.Tristate value) {
boolean r = super.setPermission(contexts, permission, value); boolean r = super.setPermission(contexts, permission, value);
save(); save();
return r; return r;
@ -73,21 +69,21 @@ public class PersistedSubjectData extends CalculatedSubjectData {
} }
@Override @Override
public boolean clearPermissions(Set<Context> context) { public boolean clearPermissions(ContextSet contexts) {
boolean r = super.clearPermissions(context); boolean r = super.clearPermissions(contexts);
save(); save();
return r; return r;
} }
@Override @Override
public boolean addParent(Set<Context> contexts, Subject parent) { public boolean addParent(ContextSet contexts, SubjectReference parent) {
boolean r = super.addParent(contexts, parent); boolean r = super.addParent(contexts, parent);
save(); save();
return r; return r;
} }
@Override @Override
public boolean removeParent(Set<Context> contexts, Subject parent) { public boolean removeParent(ContextSet contexts, SubjectReference parent) {
boolean r = super.removeParent(contexts, parent); boolean r = super.removeParent(contexts, parent);
save(); save();
return r; return r;
@ -101,21 +97,28 @@ public class PersistedSubjectData extends CalculatedSubjectData {
} }
@Override @Override
public boolean clearParents(Set<Context> contexts) { public boolean clearParents(ContextSet contexts) {
boolean r = super.clearParents(contexts); boolean r = super.clearParents(contexts);
save(); save();
return r; return r;
} }
@Override @Override
public boolean setOption(Set<Context> contexts, String key, @Nullable String value) { public boolean setOption(ContextSet contexts, String key, String value) {
boolean r = super.setOption(contexts, key, value); boolean r = super.setOption(contexts, key, value);
save(); save();
return r; return r;
} }
@Override @Override
public boolean clearOptions(Set<Context> contexts) { public boolean unsetOption(ContextSet contexts, String key) {
boolean r = super.unsetOption(contexts, key);
save();
return r;
}
@Override
public boolean clearOptions(ContextSet contexts) {
boolean r = super.clearOptions(contexts); boolean r = super.clearOptions(contexts);
save(); save();
return r; return r;

View File

@ -24,9 +24,8 @@ package me.lucko.luckperms.sponge.service.persisted;
import lombok.ToString; import lombok.ToString;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.sponge.service.data.CalculatedSubjectData; import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
import me.lucko.luckperms.sponge.service.data.SubjectReference; import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.service.context.Context;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -34,8 +33,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static me.lucko.luckperms.sponge.service.LuckPermsService.convertContexts;
/** /**
* Holds SubjectData in a "gson friendly" format for serialization * Holds SubjectData in a "gson friendly" format for serialization
*/ */
@ -45,45 +42,45 @@ public class SubjectDataHolder {
private final Map<Map<String, String>, Map<String, String>> options; private final Map<Map<String, String>, Map<String, String>> options;
private final Map<Map<String, String>, List<String>> parents; private final Map<Map<String, String>, List<String>> parents;
public SubjectDataHolder(Map<Set<Context>, Map<String, String>> options, Map<Set<Context>, Map<String, Boolean>> permissions, Map<Set<Context>, Set<SubjectReference>> parents) { public SubjectDataHolder(Map<ContextSet, Map<String, String>> options, Map<ContextSet, Map<String, Boolean>> permissions, Map<ContextSet, Set<SubjectReference>> parents) {
this.options = new HashMap<>(); this.options = new HashMap<>();
for (Map.Entry<Set<Context>, Map<String, String>> e : options.entrySet()) { for (Map.Entry<ContextSet, Map<String, String>> e : options.entrySet()) {
this.options.put(convertContexts(e.getKey()).toMap(), new HashMap<>(e.getValue())); this.options.put(e.getKey().toMap(), new HashMap<>(e.getValue()));
} }
this.permissions = new HashMap<>(); this.permissions = new HashMap<>();
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : permissions.entrySet()) { for (Map.Entry<ContextSet, Map<String, Boolean>> e : permissions.entrySet()) {
this.permissions.put(convertContexts(e.getKey()).toMap(), new HashMap<>(e.getValue())); this.permissions.put(e.getKey().toMap(), new HashMap<>(e.getValue()));
} }
this.parents = new HashMap<>(); this.parents = new HashMap<>();
for (Map.Entry<Set<Context>, Set<SubjectReference>> e : parents.entrySet()) { for (Map.Entry<ContextSet, Set<SubjectReference>> e : parents.entrySet()) {
this.parents.put(convertContexts(e.getKey()).toMap(), e.getValue().stream().map(SubjectReference::serialize).collect(Collectors.toList())); this.parents.put(e.getKey().toMap(), e.getValue().stream().map(SubjectReference::serialize).collect(Collectors.toList()));
} }
} }
public SubjectDataHolder(CalculatedSubjectData data) { public SubjectDataHolder(CalculatedSubjectData data) {
this(data.getAllOptions(), data.getAllPermissions(), data.getParents()); this(data.getOptions(), data.getPermissions(), data.getParents());
} }
public void copyTo(CalculatedSubjectData subjectData) { public void copyTo(CalculatedSubjectData subjectData) {
subjectData.replacePermissions(permissions.entrySet().stream() subjectData.replacePermissions(permissions.entrySet().stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
k -> convertContexts(ContextSet.fromMap(k.getKey())), k -> ContextSet.fromMap(k.getKey()),
Map.Entry::getValue Map.Entry::getValue
)) ))
); );
subjectData.replaceOptions(options.entrySet().stream() subjectData.replaceOptions(options.entrySet().stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
k -> convertContexts(ContextSet.fromMap(k.getKey())), k -> ContextSet.fromMap(k.getKey()),
Map.Entry::getValue Map.Entry::getValue
)) ))
); );
subjectData.replaceParents(parents.entrySet().stream() subjectData.replaceParents(parents.entrySet().stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
k -> convertContexts(ContextSet.fromMap(k.getKey())), k -> ContextSet.fromMap(k.getKey()),
v -> v.getValue().stream().map(SubjectReference::deserialize).collect(Collectors.toSet()) v -> v.getValue().stream().map(SubjectReference::deserialize).collect(Collectors.toSet())
)) ))
); );

View File

@ -0,0 +1,43 @@
/*
* 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.references;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
@Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor(staticName = "of")
public class SubjectCollectionReference {
private final String collection;
public LPSubjectCollection resolve(LuckPermsService service) {
return service.getSubjects(collection);
}
}

View File

@ -20,14 +20,16 @@
* SOFTWARE. * SOFTWARE.
*/ */
package me.lucko.luckperms.sponge.service.data; package me.lucko.luckperms.sponge.service.references;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import org.spongepowered.api.service.permission.PermissionService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.base.LPSubject;
import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.Subject;
import java.util.List; import java.util.List;
@ -49,10 +51,14 @@ public class SubjectReference {
private final String collection; private final String collection;
private final String identifier; private final String identifier;
public Subject resolve(PermissionService service) { public LPSubject resolve(LuckPermsService service) {
return service.getSubjects(collection).get(identifier); return service.getSubjects(collection).get(identifier);
} }
public LPSubjectCollection resolveCollection(LuckPermsService service) {
return service.getSubjects(collection);
}
public String serialize() { public String serialize() {
return collection + "/" + identifier; return collection + "/" + identifier;
} }

View File

@ -26,30 +26,31 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.utils.ImmutableCollectors; import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import org.spongepowered.api.service.context.Context; import me.lucko.luckperms.sponge.service.base.LPSubject;
import org.spongepowered.api.service.permission.Subject; import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
import org.spongepowered.api.service.permission.SubjectCollection; import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.util.Tristate;
import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* Super simple SubjectCollection implementation * Super simple SubjectCollection implementation
*/ */
@RequiredArgsConstructor
public class SimpleCollection implements SubjectCollection {
private final LuckPermsService service;
@Getter @Getter
@RequiredArgsConstructor
public class SimpleCollection implements LPSubjectCollection {
private final LuckPermsService service;
private final String identifier; private final String identifier;
@Getter(AccessLevel.NONE)
private final LoadingCache<String, SimpleSubject> subjects = CacheBuilder.newBuilder() private final LoadingCache<String, SimpleSubject> subjects = CacheBuilder.newBuilder()
.build(new CacheLoader<String, SimpleSubject>() { .build(new CacheLoader<String, SimpleSubject>() {
@Override @Override
@ -59,7 +60,7 @@ public class SimpleCollection implements SubjectCollection {
}); });
@Override @Override
public Subject get(@NonNull String id) { public LPSubject get(@NonNull String id) {
return subjects.getUnchecked(id.toLowerCase()); return subjects.getUnchecked(id.toLowerCase());
} }
@ -69,19 +70,14 @@ public class SimpleCollection implements SubjectCollection {
} }
@Override @Override
public Iterable<Subject> getAllSubjects() { public Collection<LPSubject> getSubjects() {
return subjects.asMap().values().stream().map(s -> (Subject) s).collect(ImmutableCollectors.toImmutableList()); return subjects.asMap().values().stream().map(s -> (LPSubject) s).collect(ImmutableCollectors.toImmutableList());
} }
@Override @Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull String id) { public Map<LPSubject, Boolean> getWithPermission(@NonNull ContextSet contexts, @NonNull String node) {
return getAllWithPermission(ImmutableSet.of(), id); ImmutableMap.Builder<LPSubject, Boolean> m = ImmutableMap.builder();
} for (LPSubject subject : subjects.asMap().values()) {
@Override
public Map<Subject, Boolean> getAllWithPermission(@NonNull Set<Context> contexts, @NonNull String node) {
ImmutableMap.Builder<Subject, Boolean> m = ImmutableMap.builder();
for (Subject subject : subjects.asMap().values()) {
Tristate ts = subject.getPermissionValue(contexts, node); Tristate ts = subject.getPermissionValue(contexts, node);
if (ts != Tristate.UNDEFINED) { if (ts != Tristate.UNDEFINED) {
m.put(subject, ts.asBoolean()); m.put(subject, ts.asBoolean());
@ -92,7 +88,7 @@ public class SimpleCollection implements SubjectCollection {
} }
@Override @Override
public Subject getDefaults() { public SubjectReference getDefaultSubject() {
return service.getDefaultSubjects().get(identifier); return SubjectReference.of("defaults", identifier);
} }
} }

View File

@ -23,20 +23,24 @@
package me.lucko.luckperms.sponge.service.simple; package me.lucko.luckperms.sponge.service.simple;
import co.aikar.timings.Timing; import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.data.CalculatedSubjectData; import me.lucko.luckperms.sponge.service.base.LPSubject;
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
import me.lucko.luckperms.sponge.service.calculated.OptionLookup;
import me.lucko.luckperms.sponge.service.calculated.PermissionLookup;
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming; import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.util.Tristate;
import java.util.ArrayList; import java.util.HashSet;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -44,35 +48,48 @@ import java.util.Set;
* Super simple Subject implementation. * Super simple Subject implementation.
*/ */
@Getter @Getter
public class SimpleSubject implements Subject { public class SimpleSubject implements LPSubject {
private final String identifier; private final String identifier;
private final LuckPermsService service; private final LuckPermsService service;
private final SimpleCollection containingCollection; private final SubjectCollectionReference parentCollection;
private final CalculatedSubjectData subjectData; private final CalculatedSubjectData subjectData;
private final CalculatedSubjectData transientSubjectData; private final CalculatedSubjectData transientSubjectData;
private final LoadingCache<PermissionLookup, Tristate> permissionLookupCache = CacheBuilder.newBuilder()
.build(new CacheLoader<PermissionLookup, Tristate>() {
@Override
public Tristate load(PermissionLookup lookup) {
return lookupPermissionValue(lookup.getContexts(), lookup.getNode());
}
});
private final LoadingCache<ContextSet, Set<SubjectReference>> parentLookupCache = CacheBuilder.newBuilder()
.build(new CacheLoader<ContextSet, Set<SubjectReference>>() {
@Override
public Set<SubjectReference> load(ContextSet contexts) {
return lookupParents(contexts);
}
});
private final LoadingCache<OptionLookup, Optional<String>> optionLookupCache = CacheBuilder.newBuilder()
.build(new CacheLoader<OptionLookup, Optional<String>>() {
@Override
public Optional<String> load(OptionLookup lookup) {
return lookupOptionValue(lookup.getContexts(), lookup.getKey());
}
});
public SimpleSubject(String identifier, LuckPermsService service, SimpleCollection containingCollection) { public SimpleSubject(String identifier, LuckPermsService service, SimpleCollection containingCollection) {
this.identifier = identifier; this.identifier = identifier;
this.service = service; this.service = service;
this.containingCollection = containingCollection; this.parentCollection = containingCollection.toReference();
this.subjectData = new CalculatedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(p)"); this.subjectData = new CalculatedSubjectData(this, service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(p)");
this.transientSubjectData = new CalculatedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(t)"); this.transientSubjectData = new CalculatedSubjectData(this, service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(t)");
service.getLocalPermissionCaches().add(permissionLookupCache);
} }
@Override private Tristate lookupPermissionValue(ContextSet contexts, String node) {
public Optional<CommandSource> getCommandSource() {
return Optional.empty();
}
@Override
public boolean hasPermission(@NonNull Set<Context> contexts, @NonNull String node) {
return getPermissionValue(contexts, node).asBoolean();
}
@Override
public Tristate getPermissionValue(@NonNull Set<Context> contexts, @NonNull String node) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_PERMISSION_VALUE)) {
Tristate res = transientSubjectData.getPermissionValue(contexts, node); Tristate res = transientSubjectData.getPermissionValue(contexts, node);
if (res != Tristate.UNDEFINED) { if (res != Tristate.UNDEFINED) {
return res; return res;
@ -83,18 +100,18 @@ public class SimpleSubject implements Subject {
return res; return res;
} }
for (Subject parent : getParents(contexts)) { for (SubjectReference parent : getParents(contexts)) {
res = parent.getPermissionValue(contexts, node); res = parent.resolve(service).getPermissionValue(contexts, node);
if (res != Tristate.UNDEFINED) { if (res != Tristate.UNDEFINED) {
return res; return res;
} }
} }
if (getContainingCollection().getIdentifier().equalsIgnoreCase("defaults")) { if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
return Tristate.UNDEFINED; return Tristate.UNDEFINED;
} }
res = getContainingCollection().getDefaults().getPermissionValue(contexts, node); res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getPermissionValue(contexts, node);
if (res != Tristate.UNDEFINED) { if (res != Tristate.UNDEFINED) {
return res; return res;
} }
@ -102,68 +119,82 @@ public class SimpleSubject implements Subject {
res = service.getDefaults().getPermissionValue(contexts, node); res = service.getDefaults().getPermissionValue(contexts, node);
return res; return res;
} }
private Set<SubjectReference> lookupParents(ContextSet contexts) {
Set<SubjectReference> s = new HashSet<>();
s.addAll(subjectData.getParents(contexts));
if (!getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")){
s.addAll(getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts));
s.addAll(service.getDefaults().getParents(contexts));
}
return ImmutableSet.copyOf(s);
}
private Optional<String> lookupOptionValue(ContextSet contexts, String key) {
Optional<String> res = Optional.ofNullable(subjectData.getOptions(contexts).get(key));
if (res.isPresent()) {
return res;
}
for (SubjectReference parent : getParents(getActiveContextSet())) {
Optional<String> tempRes = parent.resolve(service).getOption(contexts, key);
if (tempRes.isPresent()) {
return tempRes;
}
}
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
return Optional.empty();
}
res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getOption(contexts, key);
if (res.isPresent()) {
return res;
}
return service.getDefaults().getOption(contexts, key);
} }
@Override @Override
public boolean isChildOf(@NonNull Set<Context> contexts, @NonNull Subject subject) { public Tristate getPermissionValue(@NonNull ContextSet contexts, @NonNull String node) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_PERMISSION_VALUE)) {
return permissionLookupCache.getUnchecked(PermissionLookup.of(node, contexts));
}
}
@Override
public boolean isChildOf(@NonNull ContextSet contexts, @NonNull SubjectReference subject) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_IS_CHILD_OF)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_IS_CHILD_OF)) {
if (getContainingCollection().getIdentifier().equalsIgnoreCase("defaults")) { if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
return subjectData.getParents(contexts).contains(subject); return subjectData.getParents(contexts).contains(subject);
} else { } else {
return subjectData.getParents(contexts).contains(subject) || return subjectData.getParents(contexts).contains(subject) ||
getContainingCollection().getDefaults().getParents(contexts).contains(subject) || getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts).contains(subject) ||
service.getDefaults().getParents(contexts).contains(subject); service.getDefaults().getParents(contexts).contains(subject);
} }
} }
} }
@Override @Override
public List<Subject> getParents(@NonNull Set<Context> contexts) { public Set<SubjectReference> getParents(@NonNull ContextSet contexts) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_PARENTS)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_PARENTS)) {
List<Subject> s = new ArrayList<>(); return parentLookupCache.getUnchecked(contexts);
s.addAll(subjectData.getParents(contexts));
if (!getContainingCollection().getIdentifier().equalsIgnoreCase("defaults")){
s.addAll(getContainingCollection().getDefaults().getParents(contexts));
s.addAll(service.getDefaults().getParents(contexts));
}
return ImmutableList.copyOf(s);
} }
} }
@Override @Override
public Optional<String> getOption(Set<Context> set, String key) { public Optional<String> getOption(ContextSet contexts, String key) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_OPTION)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_OPTION)) {
Optional<String> res = Optional.ofNullable(subjectData.getOptions(getActiveContexts()).get(key)); return optionLookupCache.getUnchecked(OptionLookup.of(key, contexts));
if (res.isPresent()) {
return res;
}
for (Subject parent : getParents(getActiveContexts())) {
Optional<String> tempRes = parent.getOption(getActiveContexts(), key);
if (tempRes.isPresent()) {
return tempRes;
}
}
if (getContainingCollection().getIdentifier().equalsIgnoreCase("defaults")) {
return Optional.empty();
}
res = getContainingCollection().getDefaults().getOption(set, key);
if (res.isPresent()) {
return res;
}
return service.getDefaults().getOption(set, key);
} }
} }
@Override @Override
public Set<Context> getActiveContexts() { public ContextSet getActiveContextSet() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_ACTIVE_CONTEXTS)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_ACTIVE_CONTEXTS)) {
return ImmutableSet.copyOf(LuckPermsService.convertContexts(service.getPlugin().getContextManager().getApplicableContext(this))); return service.getPlugin().getContextManager().getApplicableContext(this);
} }
} }
} }