Refactor LP PermissionService to implement a modified API & add proxied classes

This commit is contained in:
Luck 2017-05-03 20:14:31 +01:00
parent 163e6e0f2a
commit 8326539d40
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
73 changed files with 2035 additions and 1254 deletions

View File

@ -153,7 +153,7 @@ public class BukkitListener implements Listener {
}
final Player player = e.getPlayer();
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
if (user == null) {

View File

@ -337,7 +337,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
for (Player player : getServer().getOnlinePlayers()) {
scheduler.doAsync(() -> {
LoginHelper.loadUser(this, player.getUniqueId(), player.getName(), false);
User user = getUserManager().get(getUuidCache().getUUID(player.getUniqueId()));
User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId()));
if (user != null) {
scheduler.doSync(() -> {
try {
@ -371,7 +371,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
player.setOp(false);
}
final User user = getUserManager().get(getUuidCache().getUUID(player.getUniqueId()));
final User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId()));
if (user != null) {
user.unregisterData();
getUserManager().unload(user);

View File

@ -172,7 +172,7 @@ public class MigrationBPermissions extends SubCommand<Object> {
// Make a LuckPerms user for the one being migrated.
plugin.getStorage().loadUser(uuid, "null").join();
User lpUser = plugin.getUserManager().get(uuid);
User lpUser = plugin.getUserManager().getIfLoaded(uuid);
migrateHolder(world, user, lpUser);

View File

@ -261,7 +261,7 @@ public class MigrationGroupManager extends SubCommand<Object> {
AtomicInteger userCount = new AtomicInteger(0);
for (Map.Entry<UUID, Set<Node>> e : users.entrySet()) {
plugin.getStorage().loadUser(e.getKey(), "null").join();
me.lucko.luckperms.common.core.model.User user = plugin.getUserManager().get(e.getKey());
me.lucko.luckperms.common.core.model.User user = plugin.getUserManager().getIfLoaded(e.getKey());
for (Node node : e.getValue()) {
user.setPermission(node);

View File

@ -191,7 +191,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
}
plugin.getStorage().loadUser(u, user.getName()).join();
User lpUser = plugin.getUserManager().get(u);
User lpUser = plugin.getUserManager().getIfLoaded(u);
try {
for (String node : user.getOwnPermissions(null)) {

View File

@ -230,7 +230,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
// Create a LuckPerms user for the UUID
plugin.getStorage().loadUser(uuid, "null").join();
User user = plugin.getUserManager().get(uuid);
User user = plugin.getUserManager().getIfLoaded(uuid);
List<Permission> permissions = joinFuture(pm.getPlayerOwnPermissions(uuid));

View File

@ -133,7 +133,7 @@ public class MigrationZPermissions extends SubCommand<Object> {
}
plugin.getStorage().loadUser(u, username).join();
User user = plugin.getUserManager().get(u);
User user = plugin.getUserManager().getIfLoaded(u);
migrateEntity(user, entity, internalService.getGroups(u));
user.getPrimaryGroup().setStoredValue(MigrationUtils.standardizeName(service.getPlayerPrimaryGroup(u)));

View File

@ -131,7 +131,7 @@ public class BungeeListener implements Listener {
@EventHandler
public void onPlayerPostLogin(PostLoginEvent e) {
final ProxiedPlayer player = e.getPlayer();
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
if (user == null) {
plugin.getProxy().getScheduler().schedule(plugin, () -> {
@ -159,7 +159,7 @@ public class BungeeListener implements Listener {
final ProxiedPlayer player = ((ProxiedPlayer) e.getSender());
User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (user == null) {
e.setHasPermission(false);
return;
@ -194,7 +194,7 @@ public class BungeeListener implements Listener {
set.add("server", plugin.getConfiguration().get(ConfigKeys.SERVER));
set.add("world", serverName);
User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(uuid));
User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(uuid));
if (user == null) {
return;
}

View File

@ -160,7 +160,7 @@ public class MigrationBungeePerms extends SubCommand<Object> {
// Make a LuckPerms user for the one being migrated.
plugin.getStorage().loadUser(u.getUUID(), u.getName()).join();
me.lucko.luckperms.common.core.model.User user = plugin.getUserManager().get(u.getUUID());
me.lucko.luckperms.common.core.model.User user = plugin.getUserManager().getIfLoaded(u.getUUID());
// Migrate global perms
for (String perm : u.getPerms()) {

View File

@ -123,7 +123,7 @@ public class ApiProvider implements LuckPermsApi {
@Override
public User getUser(@NonNull UUID uuid) {
final me.lucko.luckperms.common.core.model.User user = plugin.getUserManager().get(uuid);
final me.lucko.luckperms.common.core.model.User user = plugin.getUserManager().getIfLoaded(uuid);
return user == null ? null : user.getDelegate();
}

View File

@ -60,7 +60,7 @@ public class CheckCommand extends SingleCommand {
User user;
UUID u = Util.parseUuid(target);
if (u != null) {
user = plugin.getUserManager().get(u);
user = plugin.getUserManager().getIfLoaded(u);
} else {
user = plugin.getUserManager().getByUsername(target);
}

View File

@ -79,7 +79,7 @@ public class TreeCommand extends SingleCommand {
User user;
UUID u = Util.parseUuid(player);
if (u != null) {
user = plugin.getUserManager().get(u);
user = plugin.getUserManager().getIfLoaded(u);
} else {
user = plugin.getUserManager().getByUsername(player);
}

View File

@ -87,7 +87,7 @@ public class UserMainCommand extends MainCommand<User> {
Message.LOADING_ERROR.send(sender);
}
User user = plugin.getUserManager().get(u);
User user = plugin.getUserManager().getIfLoaded(u);
if (user == null) {
Message.LOADING_ERROR.send(sender);
return null;

View File

@ -30,7 +30,7 @@ import lombok.Getter;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.utils.ArgumentChecker;
@ -214,9 +214,9 @@ public class ArgumentUtils {
}
}
public static ContextSet handleContexts(int fromIndex, List<String> args) {
public static ImmutableContextSet handleContexts(int fromIndex, List<String> args) {
if (args.size() <= fromIndex) {
return ContextSet.empty();
return ImmutableContextSet.empty();
}
MutableContextSet contextSet = MutableContextSet.create();

View File

@ -216,7 +216,7 @@ public class Exporter implements Runnable {
List<String> output = new ArrayList<>();
plugin.getStorage().loadUser(uuid, "null").join();
User user = plugin.getUserManager().get(uuid);
User user = plugin.getUserManager().getIfLoaded(uuid);
output.add("# Export user: " + user.getUuid().toString() + " - " + user.getName().orElse("unknown username"));
boolean inDefault = false;

View File

@ -48,6 +48,8 @@ public interface Manager<I, T extends Identifiable<I>> extends Function<I, T> {
/**
* Gets or creates an object by id
*
* <p>Should only every be called by the storage implementation.</p>
*
* @param id The id to search by
* @return a {@link T} object if the object is loaded or makes and returns a new object
*/

View File

@ -47,7 +47,7 @@ public interface UserManager extends Manager<UserIdentifier, User> {
* @param uuid The uuid to search by
* @return a {@link User} object if the user is loaded, returns null if the user is not loaded
*/
User get(UUID uuid);
User getIfLoaded(UUID uuid);
/**
* Gives the user the default group if necessary.

View File

@ -123,7 +123,7 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
}
@Override
public User get(UUID uuid) {
public User getIfLoaded(UUID uuid) {
return getIfLoaded(UserIdentifier.of(uuid, null));
}
@ -142,7 +142,7 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
@Override
public void scheduleUnload(UUID uuid) {
plugin.getScheduler().doAsyncLater(() -> {
User user = get(plugin.getUuidCache().getUUID(uuid));
User user = getIfLoaded(plugin.getUuidCache().getUUID(uuid));
if (user != null && !plugin.isPlayerOnline(uuid)) {
user.unregisterData();
unload(user);

View File

@ -104,7 +104,7 @@ public class AbstractStorage implements Storage {
public CompletableFuture<Boolean> loadUser(UUID uuid, String username) {
return makeFuture(() -> {
if (backing.loadUser(uuid, username)) {
plugin.getApiProvider().getEventFactory().handleUserLoad(plugin.getUserManager().get(uuid));
plugin.getApiProvider().getEventFactory().handleUserLoad(plugin.getUserManager().getIfLoaded(uuid));
return true;
}
return false;

View File

@ -178,7 +178,7 @@ public abstract class FlatfileBacking extends AbstractBacking {
return;
}
User u = plugin.getUserManager().get(uuid);
User u = plugin.getUserManager().getIfLoaded(uuid);
if (u != null) {
plugin.getLog().info("[FileWatcher] Refreshing user " + u.getFriendlyName());
plugin.getStorage().loadUser(uuid, "null");

View File

@ -73,7 +73,7 @@ public class LoginHelper {
}
plugin.getStorage().force().loadUser(cache.getUUID(u), username).join();
User user = plugin.getUserManager().get(cache.getUUID(u));
User user = plugin.getUserManager().getIfLoaded(cache.getUUID(u));
if (user == null) {
plugin.getLog().warn("Failed to load user: " + username);
throw new RuntimeException("Failed to load user");
@ -101,7 +101,7 @@ public class LoginHelper {
}
public static void refreshPlayer(LuckPermsPlugin plugin, UUID uuid) {
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(uuid));
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(uuid));
if (user != null) {
user.getRefreshBuffer().requestDirectly();
}

View File

@ -74,8 +74,9 @@ import me.lucko.luckperms.sponge.managers.SpongeUserManager;
import me.lucko.luckperms.sponge.messaging.BungeeMessagingService;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.ServiceCacheHousekeepingTask;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.persisted.PersistedCollection;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import me.lucko.luckperms.sponge.timings.LPTimings;
import me.lucko.luckperms.sponge.utils.VersionData;
@ -99,21 +100,19 @@ import org.spongepowered.api.scheduler.SynchronousExecutor;
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 java.io.File;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.AbstractCollection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@Getter
@Plugin(id = "luckperms", name = "LuckPerms", version = VersionData.VERSION, authors = {"Luck"}, description = "A permissions plugin")
@ -283,7 +282,8 @@ public class LPSpongePlugin implements LuckPermsPlugin {
getLog().warn("Delaying LuckPerms PermissionService registration.");
lateLoad = true;
} else {
game.getServiceManager().setProvider(this, PermissionService.class, service);
game.getServiceManager().setProvider(this, LPPermissionService.class, service);
game.getServiceManager().setProvider(this, PermissionService.class, service.sponge());
game.getServiceManager().setProvider(this, LuckPermsService.class, service);
}
@ -308,7 +308,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
scheduler.doAsyncRepeating(new ExpireTemporaryTask(this), 60L);
scheduler.doAsyncRepeating(new CacheHousekeepingTask(this), 2400L);
scheduler.doAsyncRepeating(new ServiceCacheHousekeepingTask(service), 2400L);
scheduler.doAsyncRepeating(() -> userManager.performCleanup(), 2400L);
// scheduler.doAsyncRepeating(() -> userManager.performCleanup(), 2400L);
getLog().info("Successfully loaded.");
}
@ -317,7 +317,8 @@ public class LPSpongePlugin implements LuckPermsPlugin {
public void onLateEnable(GamePreInitializationEvent event) {
if (lateLoad) {
getLog().info("Providing late registration of PermissionService...");
game.getServiceManager().setProvider(this, PermissionService.class, service);
game.getServiceManager().setProvider(this, LPPermissionService.class, service);
game.getServiceManager().setProvider(this, PermissionService.class, service.sponge());
game.getServiceManager().setProvider(this, LuckPermsService.class, service);
}
}
@ -359,7 +360,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
@Override
public void onPostUpdate() {
for (LPSubjectCollection collection : service.getCollections().values()) {
for (LPSubjectCollection collection : service.getLoadedCollections().values()) {
if (collection instanceof PersistedCollection) {
((PersistedCollection) collection).loadAll();
}
@ -475,23 +476,21 @@ public class LPSpongePlugin implements LuckPermsPlugin {
@Override
public LinkedHashMap<String, Object> getExtraInfo() {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("SubjectCollection count", service.getCollections().size());
map.put("SubjectCollection count", service.getLoadedCollections().size());
map.put("Subject count",
service.getCollections().values().stream()
.map(SubjectCollection::getAllSubjects)
.flatMap(subjects -> StreamSupport.stream(subjects.spliterator(), false))
.count()
service.getLoadedCollections().values().stream()
.map(LPSubjectCollection::getLoadedSubjects)
.mapToInt(AbstractCollection::size)
.sum()
);
map.put("PermissionDescription count", service.getDescriptions().size());
return map;
}
private void registerPermission(LuckPermsService p, String node) {
Optional<PermissionDescription.Builder> builder = p.newDescriptionBuilder(this);
if (!builder.isPresent()) return;
PermissionDescription.Builder builder = p.newDescriptionBuilder(this);
try {
builder.get().assign(PermissionDescription.ROLE_ADMIN, true).description(Text.of(node)).id(node).register();
builder.assign(PermissionDescription.ROLE_ADMIN, true).description(Text.of(node)).id(node).register();
} catch (IllegalStateException e) {
e.printStackTrace();
}

View File

@ -64,7 +64,7 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory {
}
if (plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_DEFAULT_SUBJECTS)) {
processors.add(new DefaultsProcessor(plugin.getService(), contexts.getContexts()));
processors.add(new DefaultsProcessor(plugin.getService(), contexts.getContexts().makeImmutable()));
}
return registerCalculator(new PermissionCalculator(plugin, user.getFriendlyName(), processors.build()));

View File

@ -154,7 +154,7 @@ public class SpongeListener {
return;
}
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
if (user == null) {
@ -179,13 +179,13 @@ public class SpongeListener {
plugin.doAsync(() -> {
UserData data = user.getUserData();
data.preCalculate(plugin.getService().calculateContexts(context));
data.preCalculate(plugin.getService().calculateContexts(context.makeImmutable()));
for (String world : worlds) {
MutableContextSet modified = MutableContextSet.fromSet(context);
modified.removeAll("world");
modified.add("world", world);
data.preCalculate(plugin.getService().calculateContexts(modified));
data.preCalculate(plugin.getService().calculateContexts(modified.makeImmutable()));
}
});
}

View File

@ -28,7 +28,7 @@ package me.lucko.luckperms.sponge.calculators;
import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.calculators.PermissionProcessor;
import me.lucko.luckperms.sponge.service.LuckPermsService;
@ -37,11 +37,11 @@ import java.util.Map;
@AllArgsConstructor
public class DefaultsProcessor implements PermissionProcessor {
private final LuckPermsService service;
private final ContextSet contexts;
private final ImmutableContextSet contexts;
@Override
public Tristate hasPermission(String permission) {
Tristate t = service.getUserSubjects().getDefaultSubject().resolve(service).getPermissionValue(contexts, permission);
Tristate t = service.getUserSubjects().getDefaults().getPermissionValue(contexts, permission);
if (t != Tristate.UNDEFINED) {
return t;
}

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
@ -36,7 +36,7 @@ import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import java.util.List;
@ -51,7 +51,7 @@ public class OptionClear extends SubCommand<LPSubjectData> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
ContextSet contextSet = ArgumentUtils.handleContexts(0, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(0, args);
if (contextSet.isEmpty()) {
subjectData.clearOptions();
Util.sendPluginMessage(sender, "&aCleared options matching contexts &bANY&a.");

View File

@ -25,7 +25,8 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
@ -37,7 +38,7 @@ import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import java.util.List;
import java.util.Map;
@ -51,16 +52,16 @@ public class OptionInfo extends SubCommand<LPSubjectData> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
ContextSet contextSet = ArgumentUtils.handleContexts(0, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(0, args);
if (contextSet.isEmpty()) {
Util.sendPluginMessage(sender, "&aShowing options matching contexts &bANY&a.");
Map<ImmutableContextSet, Map<String, String>> options = subjectData.getOptions();
Map<ImmutableContextSet, ImmutableMap<String, String>> options = subjectData.getAllOptions();
if (options.isEmpty()) {
Util.sendPluginMessage(sender, "That subject does not have any options defined.");
return CommandResult.SUCCESS;
}
for (Map.Entry<ImmutableContextSet, Map<String, String>> e : options.entrySet()) {
for (Map.Entry<ImmutableContextSet, ImmutableMap<String, String>> e : options.entrySet()) {
Util.sendPluginMessage(sender, "&3>> &bContext: " + SpongeUtils.contextToString(e.getKey()) + "\n" + SpongeUtils.optionsToString(e.getValue()));
}

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
@ -36,7 +36,7 @@ import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import java.util.List;
@ -55,9 +55,9 @@ public class OptionSet extends SubCommand<LPSubjectData> {
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
String key = args.get(0);
String value = args.get(1);
ContextSet contextSet = ArgumentUtils.handleContexts(2, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(2, args);
if (subjectData.setOption(contextSet, key, value)) {
if (subjectData.setOption(contextSet, key, value).join()) {
Util.sendPluginMessage(sender, "&aSet &f\"" + key + "&f\"&a to &f\"" + value + "&f\"&a in context " + SpongeUtils.contextToString(contextSet));
} else {
Util.sendPluginMessage(sender, "Unable to set option. Does the Subject already have it set?");

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
@ -36,7 +36,7 @@ import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import java.util.List;
@ -53,9 +53,9 @@ public class OptionUnset extends SubCommand<LPSubjectData> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
String key = args.get(0);
ContextSet contextSet = ArgumentUtils.handleContexts(1, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(1, args);
if (subjectData.setOption(contextSet, key, null)) {
if (subjectData.unsetOption(contextSet, key).join()) {
Util.sendPluginMessage(sender, "&aUnset &f\"" + key + "&f\"&a in context " + SpongeUtils.contextToString(contextSet));
} else {
Util.sendPluginMessage(sender, "Unable to unset option. Are you sure the Subject has it set?");

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
@ -37,9 +37,9 @@ import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import org.spongepowered.api.Sponge;
@ -60,22 +60,22 @@ public class ParentAdd extends SubCommand<LPSubjectData> {
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
String collection = args.get(0);
String name = args.get(1);
ContextSet contextSet = ArgumentUtils.handleContexts(2, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(2, args);
LuckPermsService service = Sponge.getServiceManager().provideUnchecked(LuckPermsService.class);
if (service.getCollections().keySet().stream().map(String::toLowerCase).noneMatch(s -> s.equalsIgnoreCase(collection))) {
if (service.getLoadedCollections().keySet().stream().map(String::toLowerCase).noneMatch(s -> s.equalsIgnoreCase(collection))) {
Util.sendPluginMessage(sender, "Warning: SubjectCollection '&4" + collection + "&c' doesn't already exist.");
}
LPSubjectCollection c = service.getSubjects(collection);
if (!c.hasRegistered(name)) {
LPSubjectCollection c = service.getCollection(collection);
if (!c.hasRegistered(name).join()) {
Util.sendPluginMessage(sender, "Warning: Subject '&4" + name + "&c' doesn't already exist.");
}
LPSubject subject = c.get(name);
LPSubject subject = c.loadSubject(name).join();
if (subjectData.addParent(contextSet, subject.toReference())) {
Util.sendPluginMessage(sender, "&aAdded parent &b" + subject.getContainingCollection().getIdentifier() +
if (subjectData.addParent(contextSet, subject.toReference()).join()) {
Util.sendPluginMessage(sender, "&aAdded parent &b" + subject.getParentCollection().getIdentifier() +
"&a/&b" + subject.getIdentifier() + "&a in context " + SpongeUtils.contextToString(contextSet));
} else {
Util.sendPluginMessage(sender, "Unable to add parent. Does the Subject already have it added?");

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
@ -36,7 +36,7 @@ import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import java.util.List;
@ -51,7 +51,7 @@ public class ParentClear extends SubCommand<LPSubjectData> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
ContextSet contextSet = ArgumentUtils.handleContexts(0, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(0, args);
if (contextSet.isEmpty()) {
subjectData.clearParents();
Util.sendPluginMessage(sender, "&aCleared parents matching contexts &bANY&a.");

View File

@ -25,7 +25,8 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
@ -37,12 +38,11 @@ import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ParentInfo extends SubCommand<LPSubjectData> {
public ParentInfo() {
@ -53,21 +53,21 @@ public class ParentInfo extends SubCommand<LPSubjectData> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
ContextSet contextSet = ArgumentUtils.handleContexts(0, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(0, args);
if (contextSet.isEmpty()) {
Util.sendPluginMessage(sender, "&aShowing parents matching contexts &bANY&a.");
Map<ImmutableContextSet, Set<SubjectReference>> parents = subjectData.getParents();
Map<ImmutableContextSet, ImmutableList<SubjectReference>> parents = subjectData.getAllParents();
if (parents.isEmpty()) {
Util.sendPluginMessage(sender, "That subject does not have any parents defined.");
return CommandResult.SUCCESS;
}
for (Map.Entry<ImmutableContextSet, Set<SubjectReference>> e : parents.entrySet()) {
for (Map.Entry<ImmutableContextSet, ImmutableList<SubjectReference>> e : parents.entrySet()) {
Util.sendPluginMessage(sender, "&3>> &bContext: " + SpongeUtils.contextToString(e.getKey()) + "\n" + SpongeUtils.parentsToString(e.getValue()));
}
} else {
Set<SubjectReference> parents = subjectData.getParents(contextSet);
List<SubjectReference> parents = subjectData.getParents(contextSet);
if (parents.isEmpty()) {
Util.sendPluginMessage(sender, "That subject does not have any parents defined in those contexts.");
return CommandResult.SUCCESS;

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
@ -37,9 +37,9 @@ import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import org.spongepowered.api.Sponge;
@ -60,22 +60,22 @@ public class ParentRemove extends SubCommand<LPSubjectData> {
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
String collection = args.get(0);
String name = args.get(1);
ContextSet contextSet = ArgumentUtils.handleContexts(2, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(2, args);
LuckPermsService service = Sponge.getServiceManager().provideUnchecked(LuckPermsService.class);
if (service.getCollections().keySet().stream().map(String::toLowerCase).noneMatch(s -> s.equalsIgnoreCase(collection))) {
if (service.getLoadedCollections().keySet().stream().map(String::toLowerCase).noneMatch(s -> s.equalsIgnoreCase(collection))) {
Util.sendPluginMessage(sender, "Warning: SubjectCollection '&4" + collection + "&c' doesn't exist.");
}
LPSubjectCollection c = service.getSubjects(collection);
if (!c.hasRegistered(name)) {
LPSubjectCollection c = service.getCollection(collection);
if (!c.hasRegistered(name).join()) {
Util.sendPluginMessage(sender, "Warning: Subject '&4" + name + "&c' doesn't exist.");
}
LPSubject subject = c.get(name);
LPSubject subject = c.loadSubject(name).join();
if (subjectData.removeParent(contextSet, subject.toReference())) {
Util.sendPluginMessage(sender, "&aRemoved parent &b" + subject.getContainingCollection().getIdentifier() +
if (subjectData.removeParent(contextSet, subject.toReference()).join()) {
Util.sendPluginMessage(sender, "&aRemoved parent &b" + subject.getParentCollection().getIdentifier() +
"&a/&b" + subject.getIdentifier() + "&a in context " + SpongeUtils.contextToString(contextSet));
} else {
Util.sendPluginMessage(sender, "Unable to remove parent. Are you sure the Subject has it added?");

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
@ -36,7 +36,7 @@ import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import java.util.List;
@ -51,7 +51,7 @@ public class PermissionClear extends SubCommand<LPSubjectData> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
ContextSet contextSet = ArgumentUtils.handleContexts(0, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(0, args);
if (contextSet.isEmpty()) {
subjectData.clearPermissions();
Util.sendPluginMessage(sender, "&aCleared permissions matching contexts &bANY&a.");

View File

@ -25,7 +25,8 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.context.ContextSet;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
@ -37,7 +38,7 @@ import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import java.util.List;
import java.util.Map;
@ -51,16 +52,16 @@ public class PermissionInfo extends SubCommand<LPSubjectData> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
ContextSet contextSet = ArgumentUtils.handleContexts(0, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(0, args);
if (contextSet.isEmpty()) {
Util.sendPluginMessage(sender, "&aShowing permissions matching contexts &bANY&a.");
Map<ImmutableContextSet, Map<String, Boolean>> permissions = subjectData.getPermissions();
Map<ImmutableContextSet, ImmutableMap<String, Boolean>> permissions = subjectData.getAllPermissions();
if (permissions.isEmpty()) {
Util.sendPluginMessage(sender, "That subject does not have any permissions defined.");
return CommandResult.SUCCESS;
}
for (Map.Entry<ImmutableContextSet, Map<String, Boolean>> e : permissions.entrySet()) {
for (Map.Entry<ImmutableContextSet, ImmutableMap<String, Boolean>> e : permissions.entrySet()) {
Util.sendPluginMessage(sender, "&3>> &bContext: " + SpongeUtils.contextToString(e.getKey()) + "\n" + SpongeUtils.nodesToString(e.getValue()));
}

View File

@ -26,7 +26,7 @@
package me.lucko.luckperms.sponge.commands;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
@ -37,7 +37,7 @@ import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import java.util.List;
@ -56,9 +56,9 @@ public class PermissionSet extends SubCommand<LPSubjectData> {
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List<String> args, String label) throws CommandException {
String node = args.get(0);
Tristate tristate = SpongeUtils.parseTristate(1, args);
ContextSet contextSet = ArgumentUtils.handleContexts(2, args);
ImmutableContextSet contextSet = ArgumentUtils.handleContexts(2, args);
if (subjectData.setPermission(contextSet, node, tristate)) {
if (subjectData.setPermission(contextSet, node, tristate).join()) {
Util.sendPluginMessage(sender, "&aSet &b" + node + "&a to &b" + tristate.toString().toLowerCase() + "&a in context " + SpongeUtils.contextToString(contextSet));
} else {
Util.sendPluginMessage(sender, "Unable to set permission. Does the Subject already have it set?");

View File

@ -41,9 +41,9 @@ import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import java.util.ArrayList;
import java.util.Arrays;
@ -100,7 +100,7 @@ public class SpongeMainCommand extends Command<Void, LPSubjectData> {
if (args.size() < 1) {
Util.sendPluginMessage(sender, "&aCurrent Subject Collections:\n" +
Util.toCommaSep(service.getCollections().keySet().stream()
Util.toCommaSep(service.getLoadedCollections().keySet().stream()
.filter(s -> !s.equalsIgnoreCase("user") && !s.equalsIgnoreCase("group"))
.sorted()
.collect(Collectors.toList())
@ -116,14 +116,14 @@ public class SpongeMainCommand extends Command<Void, LPSubjectData> {
return CommandResult.STATE_ERROR;
}
if (service.getCollections().keySet().stream().map(String::toLowerCase).noneMatch(s -> s.equalsIgnoreCase(subjectCollection))) {
if (service.getLoadedCollections().keySet().stream().map(String::toLowerCase).noneMatch(s -> s.equalsIgnoreCase(subjectCollection))) {
Util.sendPluginMessage(sender, "Warning: SubjectCollection '&4" + subjectCollection + "&c' doesn't already exist. Creating it now.");
}
LPSubjectCollection collection = service.getSubjects(subjectCollection);
LPSubjectCollection collection = service.getCollection(subjectCollection);
if (args.size() < 2) {
List<String> subjects = collection.getSubjects().stream()
List<String> subjects = collection.getLoadedSubjects().stream()
.map(LPSubject::getIdentifier)
.collect(Collectors.toList());
@ -183,11 +183,11 @@ public class SpongeMainCommand extends Command<Void, LPSubjectData> {
}
String subjectId = args.get(1);
if (!collection.hasRegistered(subjectId)) {
if (!collection.hasRegistered(subjectId).join()) {
Util.sendPluginMessage(sender, "Warning: Subject '&4" + subjectId + "&c' doesn't already exist. Creating it now.");
}
LPSubject subject = collection.get(subjectId);
LPSubject subject = collection.loadSubject(subjectId).join();
LPSubjectData subjectData = persistent ? subject.getSubjectData() : subject.getTransientSubjectData();
CommandResult result;

View File

@ -35,7 +35,6 @@ import me.lucko.luckperms.sponge.service.references.SubjectReference;
import java.util.List;
import java.util.Map;
import java.util.Set;
@UtilityClass
public class SpongeUtils {
@ -77,7 +76,7 @@ public class SpongeUtils {
return sb.toString();
}
public static String parentsToString(Set<SubjectReference> parents) {
public static String parentsToString(Iterable<SubjectReference> parents) {
StringBuilder sb = new StringBuilder();
for (SubjectReference s : parents) {
sb.append("&3> &a")

View File

@ -29,7 +29,7 @@ import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.sponge.service.proxy.Util;
import me.lucko.luckperms.sponge.service.model.CompatibilityUtil;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
@ -46,7 +46,7 @@ public class SpongeCalculatorLink implements ContextCalculator<Subject> {
Set<Context> contexts = new HashSet<>();
calculator.accumulateContexts(subject, contexts);
accumulator.addAll(Util.convertContexts(contexts));
accumulator.addAll(CompatibilityUtil.convertContexts(contexts));
return accumulator;
}
}

View File

@ -26,41 +26,50 @@
package me.lucko.luckperms.sponge.managers;
import lombok.Getter;
import lombok.NonNull;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import me.lucko.luckperms.api.HeldPermission;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.event.cause.CreationCause;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.utils.ArgumentChecker;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.model.SpongeGroup;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.proxy.SubjectCollectionProxy;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.SubjectCollection;
import co.aikar.timings.Timing;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
@Getter
private final LPSpongePlugin plugin;
private SubjectCollectionProxy spongeProxy = null;
private final LoadingCache<String, SpongeGroup> objects = Caffeine.newBuilder()
.build(new CacheLoader<String, SpongeGroup>() {
@Override
@ -77,20 +86,26 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
private final LoadingCache<String, LPSubject> subjectLoadingCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build(s -> {
if (isLoaded(s)) {
return getIfLoaded(s).getSpongeData();
SpongeGroup group = getIfLoaded(s);
if (group != null) {
// they're already loaded, but the data might not actually be there yet
// if stuff is being loaded, then the user's i/o lock will be locked by the storage impl
group.getIoLock().lock();
group.getIoLock().unlock();
return group.sponge();
}
// Request load
getPlugin().getStorage().createAndLoadGroup(s, CreationCause.INTERNAL).join();
SpongeGroup group = getIfLoaded(s);
group = getIfLoaded(s);
if (group == null) {
getPlugin().getLog().severe("Error whilst loading group '" + s + "'.");
throw new RuntimeException();
}
return group.getSpongeData();
return group.sponge();
});
public SpongeGroupManager(LPSpongePlugin plugin) {
@ -150,8 +165,11 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
* ------------------------------------------ */
@Override
public String getIdentifier() {
return PermissionService.SUBJECTS_GROUP;
public synchronized SubjectCollection sponge() {
if (spongeProxy == null) {
spongeProxy = new SubjectCollectionProxy(Preconditions.checkNotNull(plugin.getService(), "service"), this);
}
return spongeProxy;
}
@Override
@ -160,52 +178,119 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
}
@Override
public LPSubject get(@NonNull String id) {
// Special Sponge method. This call will actually load the group from the datastore if not already present.
public String getIdentifier() {
return PermissionService.SUBJECTS_GROUP;
}
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_COLLECTION_GET)) {
id = id.toLowerCase();
if (ArgumentChecker.checkNameWithSpace(id)) {
plugin.getLog().warn("Couldn't get group subject for id: " + id + " (invalid name)");
return plugin.getService().getFallbackGroupSubjects().get(id); // fallback to transient collection
}
@Override
public Predicate<String> getIdentifierValidityPredicate() {
// TODO change this to use the actual limitations
return Predicates.alwaysTrue();
}
try {
return subjectLoadingCache.get(id);
} catch (Exception e) {
e.printStackTrace();
plugin.getLog().warn("Couldn't get group subject for id: " + id);
return plugin.getService().getFallbackGroupSubjects().get(id); // fallback to the transient collection
}
@Override
public CompletableFuture<LPSubject> loadSubject(String identifier) {
LPSubject present = subjectLoadingCache.getIfPresent(identifier.toLowerCase());
if (present != null) {
return CompletableFuture.completedFuture(present);
}
return CompletableFuture.supplyAsync(() -> subjectLoadingCache.get(identifier.toLowerCase()), plugin.getScheduler().getAsyncExecutor());
}
@Override
public Optional<LPSubject> getSubject(String identifier) {
return Optional.ofNullable(getIfLoaded(identifier.toLowerCase())).map(SpongeGroup::sponge);
}
@Override
public CompletableFuture<Boolean> hasRegistered(String identifier) {
if (isLoaded(identifier.toLowerCase())) {
return CompletableFuture.completedFuture(true);
} else {
return CompletableFuture.completedFuture(false);
}
}
@Override
public boolean hasRegistered(@NonNull String id) {
id = id.toLowerCase();
return !ArgumentChecker.checkName(id) && isLoaded(id);
public CompletableFuture<ImmutableCollection<LPSubject>> loadSubjects(Set<String> identifiers) {
return CompletableFuture.supplyAsync(() -> {
ImmutableSet.Builder<LPSubject> ret = ImmutableSet.builder();
for (String id : identifiers) {
ret.add(loadSubject(id.toLowerCase()).join());
}
return ret.build();
}, plugin.getScheduler().getAsyncExecutor());
}
@Override
public Collection<LPSubject> getSubjects() {
return objects.asMap().values().stream().map(SpongeGroup::getSpongeData).collect(ImmutableCollectors.toImmutableList());
public ImmutableCollection<LPSubject> getLoadedSubjects() {
return getAll().values().stream().map(SpongeGroup::sponge).collect(ImmutableCollectors.toImmutableSet());
}
@Override
public Map<LPSubject, Boolean> getWithPermission(@NonNull ContextSet contexts, @NonNull String node) {
public CompletableFuture<ImmutableSet<String>> getAllIdentifiers() {
return CompletableFuture.completedFuture(ImmutableSet.copyOf(getAll().keySet()));
}
@Override
public CompletableFuture<ImmutableMap<SubjectReference, Boolean>> getAllWithPermission(String permission) {
return CompletableFuture.supplyAsync(() -> {
ImmutableMap.Builder<SubjectReference, Boolean> ret = ImmutableMap.builder();
List<HeldPermission<String>> lookup = plugin.getStorage().getGroupsWithPermission(permission).join();
for (HeldPermission<String> holder : lookup) {
if (holder.asNode().getFullContexts().equals(ImmutableContextSet.empty())) {
ret.put(getService().newSubjectReference(getIdentifier(), holder.getHolder()), holder.getValue());
}
}
return ret.build();
}, plugin.getScheduler().getAsyncExecutor());
}
@Override
public CompletableFuture<ImmutableMap<SubjectReference, Boolean>> getAllWithPermission(ImmutableContextSet contexts, String permission) {
return CompletableFuture.supplyAsync(() -> {
ImmutableMap.Builder<SubjectReference, Boolean> ret = ImmutableMap.builder();
List<HeldPermission<String>> lookup = plugin.getStorage().getGroupsWithPermission(permission).join();
for (HeldPermission<String> holder : lookup) {
if (holder.asNode().getFullContexts().equals(contexts)) {
ret.put(getService().newSubjectReference(getIdentifier(), holder.getHolder()), holder.getValue());
}
}
return ret.build();
}, plugin.getScheduler().getAsyncExecutor());
}
@Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) {
return objects.asMap().values().stream()
.map(SpongeGroup::getSpongeData)
.filter(sub -> sub.getPermissionValue(contexts, node) != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(contexts, node).asBoolean()));
.map(SpongeGroup::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, sub -> sub.getValue().asBoolean()));
}
@Override
public SubjectReference getDefaultSubject() {
return SubjectReference.of("defaults", getIdentifier());
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) {
return objects.asMap().values().stream()
.map(SpongeGroup::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, sub -> sub.getValue().asBoolean()));
}
@Override
public boolean getTransientHasPriority() {
return true;
public LPSubject getDefaults() {
return getService().getDefaultSubjects().loadSubject(getIdentifier()).join();
}
@Override
public void suggestUnload(String identifier) {
// noop
}
}

View File

@ -26,16 +26,19 @@
package me.lucko.luckperms.sponge.managers;
import lombok.Getter;
import lombok.NonNull;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import me.lucko.luckperms.api.HeldPermission;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.core.UserIdentifier;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.managers.UserManager;
@ -44,28 +47,30 @@ import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.model.SpongeUser;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.proxy.SubjectCollectionProxy;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.SubjectCollection;
import co.aikar.timings.Timing;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
public class SpongeUserManager implements UserManager, LPSubjectCollection {
@Getter
private final LPSpongePlugin plugin;
private SubjectCollectionProxy spongeProxy = null;
private final LoadingCache<UserIdentifier, SpongeUser> objects = Caffeine.newBuilder()
.build(new CacheLoader<UserIdentifier, SpongeUser>() {
@Override
@ -82,23 +87,29 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
private final LoadingCache<UUID, LPSubject> subjectLoadingCache = Caffeine.<UUID, LPSubject>newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build(u -> {
if (isLoaded(UserIdentifier.of(u, null))) {
SpongeUser user = get(u);
// check if the user instance is already loaded.
SpongeUser user = getIfLoaded(u);
if (user != null) {
// they're already loaded, but the data might not actually be there yet
// if stuff is being loaded, then the user's i/o lock will be locked by the storage impl
user.getIoLock().lock();
user.getIoLock().unlock();
// ok, data is here, let's do the pre-calculation stuff.
user.preCalculateData(false);
return get(u).getSpongeData();
return user.sponge();
}
// Request load
getPlugin().getStorage().loadUser(u, "null").join();
SpongeUser user = get(u);
getPlugin().getStorage().loadUser(u, null).join();
user = getIfLoaded(u);
if (user == null) {
getPlugin().getLog().severe("Error whilst loading user '" + u + "'.");
throw new RuntimeException();
}
user.preCalculateData(false);
return user.getSpongeData();
return user.sponge();
});
public SpongeUserManager(LPSpongePlugin plugin) {
@ -112,18 +123,6 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
new SpongeUser(id.getUuid(), id.getUsername().get(), plugin);
}
public void performCleanup() {
Set<UserIdentifier> set = new HashSet<>();
for (Map.Entry<UserIdentifier, SpongeUser> user : objects.asMap().entrySet()) {
if (user.getValue().getSpongeData().shouldCleanup()) {
user.getValue().unregisterData();
set.add(user.getKey());
}
}
objects.invalidateAll(set);
}
/* ------------------------------------------
* Manager methods
* ------------------------------------------ */
@ -183,7 +182,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
}
@Override
public SpongeUser get(UUID uuid) {
public SpongeUser getIfLoaded(UUID uuid) {
return getIfLoaded(UserIdentifier.of(uuid, null));
}
@ -220,8 +219,11 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
* ------------------------------------------ */
@Override
public String getIdentifier() {
return PermissionService.SUBJECTS_USER;
public synchronized SubjectCollection sponge() {
if (spongeProxy == null) {
spongeProxy = new SubjectCollectionProxy(Preconditions.checkNotNull(plugin.getService(), "service"), this);
}
return spongeProxy;
}
@Override
@ -230,58 +232,166 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
}
@Override
public LPSubject get(@NonNull String id) {
// Special Sponge method. This call will actually load the user from the datastore if not already present.
public String getIdentifier() {
return PermissionService.SUBJECTS_USER;
}
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_COLLECTION_GET)) {
UUID uuid = Util.parseUuid(id);
if (uuid == null) {
plugin.getLog().warn("Couldn't get user subject for id: " + id + " (not a uuid)");
return plugin.getService().getFallbackUserSubjects().get(id); // fallback to the transient collection
}
UUID u = plugin.getUuidCache().getUUID(uuid);
@Override
public Predicate<String> getIdentifierValidityPredicate() {
return s -> {
try {
return subjectLoadingCache.get(u);
} catch (Exception e) {
e.printStackTrace();
plugin.getLog().warn("Couldn't get user subject for id: " + id);
return plugin.getService().getFallbackUserSubjects().get(id); // fallback to the transient collection
//noinspection ResultOfMethodCallIgnored
UUID.fromString(s);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
};
}
@Override
public boolean hasRegistered(@NonNull String id) {
UUID uuid = Util.parseUuid(id);
public CompletableFuture<LPSubject> loadSubject(String identifier) {
UUID uuid;
try {
uuid = UUID.fromString(identifier);
} catch (IllegalArgumentException e) {
CompletableFuture<LPSubject> fut = new CompletableFuture<>();
fut.completeExceptionally(e);
return fut;
}
LPSubject present = subjectLoadingCache.getIfPresent(uuid);
if (present != null) {
return CompletableFuture.completedFuture(present);
}
return CompletableFuture.supplyAsync(() -> subjectLoadingCache.get(uuid), plugin.getScheduler().getAsyncExecutor());
}
@Override
public Optional<LPSubject> getSubject(String identifier) {
UUID uuid = UUID.fromString(identifier);
return Optional.ofNullable(getIfLoaded(uuid)).map(SpongeUser::sponge);
}
@Override
public CompletableFuture<Boolean> hasRegistered(String identifier) {
UUID uuid = null;
IllegalArgumentException ex = null;
try {
uuid = UUID.fromString(identifier);
} catch (IllegalArgumentException e) {
ex = e;
}
if (uuid != null && isLoaded(UserIdentifier.of(uuid, null))) {
return CompletableFuture.completedFuture(true);
}
if (uuid == null) {
return false;
CompletableFuture<Boolean> fut = new CompletableFuture<>();
fut.completeExceptionally(ex);
return fut;
}
UUID internal = plugin.getUuidCache().getUUID(uuid);
return isLoaded(UserIdentifier.of(internal, null));
UUID finalUuid = uuid;
return plugin.getStorage().getUniqueUsers().thenApply(set -> set.contains(finalUuid));
}
@Override
public Collection<LPSubject> getSubjects() {
return objects.asMap().values().stream().map(SpongeUser::getSpongeData).collect(ImmutableCollectors.toImmutableList());
public CompletableFuture<ImmutableCollection<LPSubject>> loadSubjects(Set<String> identifiers) {
return CompletableFuture.supplyAsync(() -> {
ImmutableSet.Builder<LPSubject> ret = ImmutableSet.builder();
for (String id : identifiers) {
UUID uuid;
try {
uuid = UUID.fromString(id);
} catch (IllegalArgumentException e) {
continue;
}
ret.add(loadSubject(uuid.toString()).join());
}
return ret.build();
}, plugin.getScheduler().getAsyncExecutor());
}
@Override
public Map<LPSubject, Boolean> getWithPermission(@NonNull ContextSet contexts, @NonNull String node) {
public ImmutableCollection<LPSubject> getLoadedSubjects() {
return getAll().values().stream().map(SpongeUser::sponge).collect(ImmutableCollectors.toImmutableSet());
}
@Override
public CompletableFuture<ImmutableSet<String>> getAllIdentifiers() {
return CompletableFuture.supplyAsync(() -> {
ImmutableSet.Builder<String> ids = ImmutableSet.builder();
getAll().keySet().forEach(uuid -> ids.add(uuid.getUuid().toString()));
plugin.getStorage().getUniqueUsers().join().forEach(uuid -> ids.add(uuid.toString()));
return ids.build();
}, plugin.getScheduler().getAsyncExecutor());
}
@Override
public CompletableFuture<ImmutableMap<SubjectReference, Boolean>> getAllWithPermission(String permission) {
return CompletableFuture.supplyAsync(() -> {
ImmutableMap.Builder<SubjectReference, Boolean> ret = ImmutableMap.builder();
List<HeldPermission<UUID>> lookup = plugin.getStorage().getUsersWithPermission(permission).join();
for (HeldPermission<UUID> holder : lookup) {
if (holder.asNode().getFullContexts().equals(ImmutableContextSet.empty())) {
ret.put(getService().newSubjectReference(getIdentifier(), holder.getHolder().toString()), holder.getValue());
}
}
return ret.build();
}, plugin.getScheduler().getAsyncExecutor());
}
@Override
public CompletableFuture<ImmutableMap<SubjectReference, Boolean>> getAllWithPermission(ImmutableContextSet contexts, String permission) {
return CompletableFuture.supplyAsync(() -> {
ImmutableMap.Builder<SubjectReference, Boolean> ret = ImmutableMap.builder();
List<HeldPermission<UUID>> lookup = plugin.getStorage().getUsersWithPermission(permission).join();
for (HeldPermission<UUID> holder : lookup) {
if (holder.asNode().getFullContexts().equals(contexts)) {
ret.put(getService().newSubjectReference(getIdentifier(), holder.getHolder().toString()), holder.getValue());
}
}
return ret.build();
}, plugin.getScheduler().getAsyncExecutor());
}
@Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) {
return objects.asMap().values().stream()
.map(SpongeUser::getSpongeData)
.filter(sub -> sub.getPermissionValue(contexts, node) != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(sub -> sub, sub -> sub.getPermissionValue(contexts, node).asBoolean()));
.map(SpongeUser::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(ImmutableContextSet.empty(), permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, sub -> sub.getValue().asBoolean()));
}
@Override
public SubjectReference getDefaultSubject() {
return SubjectReference.of("defaults", getIdentifier());
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) {
return objects.asMap().values().stream()
.map(SpongeUser::sponge)
.map(sub -> Maps.immutableEntry(sub, sub.getPermissionValue(contexts, permission)))
.filter(pair -> pair.getValue() != Tristate.UNDEFINED)
.collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, sub -> sub.getValue().asBoolean()));
}
@Override
public boolean getTransientHasPriority() {
return true;
public LPSubject getDefaults() {
return getService().getDefaultSubjects().loadSubject(getIdentifier()).join();
}
@Override
public void suggestUnload(String identifier) {
// noop
}
}

View File

@ -93,10 +93,10 @@ public class MigrationPermissionManager extends SubCommand<Object> {
for (SubjectCollection collection : pmService.getKnownSubjects().values()) {
SpongeMigrationUtils.migrateSubjectData(
collection.getDefaults().getSubjectData(),
lpService.getSubjects("defaults").get(collection.getIdentifier()).getSubjectData()
lpService.getCollection("defaults").loadSubject(collection.getIdentifier()).join().sponge().getSubjectData()
);
}
SpongeMigrationUtils.migrateSubjectData(pmService.getDefaults().getSubjectData(), lpService.getDefaults().getSubjectData());
SpongeMigrationUtils.migrateSubjectData(pmService.getDefaults().getSubjectData(), lpService.getDefaults().sponge().getSubjectData());
// Migrate groups
log.log("Starting group migration.");
@ -126,7 +126,7 @@ public class MigrationPermissionManager extends SubCommand<Object> {
// Make a LuckPerms user for the one being migrated
plugin.getStorage().loadUser(uuid, "null").join();
User user = plugin.getUserManager().get(uuid);
User user = plugin.getUserManager().getIfLoaded(uuid);
if (user.getNodes().size() <= 1) {
user.clearNodes(false);
}

View File

@ -90,10 +90,10 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
for (SubjectCollection collection : pexService.getKnownSubjects().values()) {
SpongeMigrationUtils.migrateSubjectData(
collection.getDefaults().getSubjectData(),
lpService.getSubjects("defaults").get(collection.getIdentifier()).getSubjectData()
lpService.getCollection("defaults").loadSubject(collection.getIdentifier()).join().sponge().getSubjectData()
);
}
SpongeMigrationUtils.migrateSubjectData(pexService.getDefaults().getSubjectData(), lpService.getDefaults().getSubjectData());
SpongeMigrationUtils.migrateSubjectData(pexService.getDefaults().getSubjectData(), lpService.getDefaults().sponge().getSubjectData());
log.log("Calculating group weightings.");
int maxWeight = 0;
@ -182,7 +182,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
// Make a LuckPerms user for the one being migrated
plugin.getStorage().loadUser(uuid, "null").join();
User user = plugin.getUserManager().get(uuid);
User user = plugin.getUserManager().getIfLoaded(uuid);
if (user.getNodes().size() <= 1) {
user.clearNodes(false);
}

View File

@ -32,7 +32,7 @@ import me.lucko.luckperms.common.commands.impl.migration.MigrationUtils;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.sponge.service.proxy.Util;
import me.lucko.luckperms.sponge.service.model.CompatibilityUtil;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.PermissionService;
@ -55,7 +55,7 @@ public class SpongeMigrationUtils {
// Migrate permissions
Map<Set<Context>, Map<String, Boolean>> perms = subject.getSubjectData().getAllPermissions();
for (Map.Entry<Set<Context>, Map<String, Boolean>> e : perms.entrySet()) {
ContextSet context = Util.convertContexts(e.getKey());
ContextSet context = CompatibilityUtil.convertContexts(e.getKey());
for (Map.Entry<String, Boolean> perm : e.getValue().entrySet()) {
if (perm.getKey().isEmpty()) {
@ -69,7 +69,7 @@ public class SpongeMigrationUtils {
// Migrate options
Map<Set<Context>, Map<String, String>> opts = subject.getSubjectData().getAllOptions();
for (Map.Entry<Set<Context>, Map<String, String>> e : opts.entrySet()) {
ContextSet context = Util.convertContexts(e.getKey());
ContextSet context = CompatibilityUtil.convertContexts(e.getKey());
for (Map.Entry<String, String> opt : e.getValue().entrySet()) {
if (opt.getKey().isEmpty() || opt.getValue().isEmpty()) {
@ -89,7 +89,7 @@ public class SpongeMigrationUtils {
// Migrate parents
Map<Set<Context>, List<Subject>> parents = subject.getSubjectData().getAllParents();
for (Map.Entry<Set<Context>, List<Subject>> e : parents.entrySet()) {
ContextSet context = Util.convertContexts(e.getKey());
ContextSet context = CompatibilityUtil.convertContexts(e.getKey());
for (Subject s : e.getValue()) {
if (!s.getContainingCollection().getIdentifier().equalsIgnoreCase(PermissionService.SUBJECTS_GROUP)) {

View File

@ -29,21 +29,21 @@ import lombok.Getter;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.LocalizedNode;
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.ImmutableContextSet;
import me.lucko.luckperms.common.caching.MetaAccumulator;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.LuckPermsSubjectData;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.Util;
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
import me.lucko.luckperms.sponge.service.model.CompatibilityUtil;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming;
@ -61,7 +61,6 @@ import java.util.stream.Collectors;
public class SpongeGroup extends Group {
@Getter
private final GroupSubject spongeData;
public SpongeGroup(String name, LPSpongePlugin plugin) {
@ -69,6 +68,10 @@ public class SpongeGroup extends Group {
this.spongeData = new GroupSubject(plugin, this);
}
public GroupSubject sponge() {
return this.spongeData;
}
public static class GroupSubject implements LPSubject {
@Getter
@ -83,7 +86,7 @@ public class SpongeGroup extends Group {
@Getter
private final LuckPermsSubjectData transientSubjectData;
private final LoadingCache<ContextSet, NodeTree> permissionCache = Caffeine.newBuilder()
private final LoadingCache<ImmutableContextSet, NodeTree> permissionCache = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(contexts -> {
// TODO move this away from NodeTree
@ -94,21 +97,21 @@ public class SpongeGroup extends Group {
return NodeTree.of(permissions);
});
private final LoadingCache<ContextSet, Set<SubjectReference>> parentCache = Caffeine.newBuilder()
private final LoadingCache<ImmutableContextSet, ImmutableList<SubjectReference>> parentCache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(contexts -> {
Set<SubjectReference> subjects = getParent().getAllNodes(ExtractedContexts.generate(getPlugin().getService().calculateContexts(contexts))).stream()
.map(LocalizedNode::getNode)
.filter(Node::isGroupNode)
.map(Node::getGroupName)
.map(s -> getPlugin().getService().getGroupSubjects().get(s))
.map(s -> getPlugin().getService().getGroupSubjects().loadSubject(s).join())
.map(LPSubject::toReference)
.collect(Collectors.toSet());
subjects.addAll(getPlugin().getService().getGroupSubjects().getDefaultSubject().resolve(getService()).getParents(contexts));
subjects.addAll(getPlugin().getService().getGroupSubjects().getDefaults().getParents(contexts));
subjects.addAll(getPlugin().getService().getDefaults().getParents(contexts));
return ImmutableSet.copyOf(subjects);
return getService().sortSubjects(subjects);
});
private GroupSubject(LPSpongePlugin plugin, SpongeGroup parent) {
@ -147,8 +150,8 @@ public class SpongeGroup extends Group {
}
@Override
public SubjectCollectionReference getParentCollection() {
return plugin.getService().getGroupSubjects().toReference();
public LPSubjectCollection getParentCollection() {
return plugin.getService().getGroupSubjects();
}
@Override
@ -157,15 +160,15 @@ public class SpongeGroup extends Group {
}
@Override
public Tristate getPermissionValue(ContextSet contexts, String permission) {
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PERMISSION_VALUE)) {
NodeTree nt = permissionCache.get(contexts);
Tristate t = Util.convertTristate(nt.get(permission));
Tristate t = CompatibilityUtil.convertTristate(nt.get(permission));
if (t != Tristate.UNDEFINED) {
return t;
}
t = plugin.getService().getGroupSubjects().getDefaultSubject().resolve(getService()).getPermissionValue(contexts, permission);
t = plugin.getService().getGroupSubjects().getDefaults().getPermissionValue(contexts, permission);
if (t != Tristate.UNDEFINED) {
return t;
}
@ -176,21 +179,21 @@ public class SpongeGroup extends Group {
}
@Override
public boolean isChildOf(ContextSet contexts, SubjectReference parent) {
public boolean isChildOf(ImmutableContextSet contexts, SubjectReference parent) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_IS_CHILD_OF)) {
return parent.getCollection().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean();
}
}
@Override
public Set<SubjectReference> getParents(ContextSet contexts) {
public ImmutableList<SubjectReference> getParents(ImmutableContextSet contexts) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PARENTS)) {
return parentCache.get(contexts);
}
}
@Override
public Optional<String> getOption(ContextSet contexts, String s) {
public Optional<String> getOption(ImmutableContextSet contexts, String s) {
try (Timing ignored = plugin.getService().getPlugin().getTimings().time(LPTiming.GROUP_GET_OPTION)) {
Optional<String> option;
if (s.equalsIgnoreCase("prefix")) {
@ -207,7 +210,7 @@ public class SpongeGroup extends Group {
return option;
}
option = plugin.getService().getGroupSubjects().getDefaultSubject().resolve(getService()).getOption(contexts, s);
option = plugin.getService().getGroupSubjects().getDefaults().getOption(contexts, s);
if (option.isPresent()) {
return option;
}
@ -217,13 +220,13 @@ public class SpongeGroup extends Group {
}
@Override
public ContextSet getActiveContextSet() {
public ImmutableContextSet getActiveContextSet() {
try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_ACTIVE_CONTEXTS)) {
return plugin.getContextManager().getApplicableContext(this);
return plugin.getContextManager().getApplicableContext(this.sponge()).makeImmutable();
}
}
private Optional<String> getChatMeta(ContextSet contexts, boolean prefix) {
private Optional<String> getChatMeta(ImmutableContextSet contexts, boolean prefix) {
MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, ExtractedContexts.generate(plugin.getService().calculateContexts(contexts)));
if (prefix) {
return Optional.ofNullable(metaAccumulator.getPrefixStack().toFormattedString());
@ -232,7 +235,7 @@ public class SpongeGroup extends Group {
}
}
private Optional<String> getMeta(ContextSet contexts, String key) {
private Optional<String> getMeta(ImmutableContextSet contexts, String key) {
MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, ExtractedContexts.generate(plugin.getService().calculateContexts(contexts)));
Map<String, String> meta = metaAccumulator.getMeta();
return Optional.ofNullable(meta.get(key));

View File

@ -27,17 +27,18 @@ package me.lucko.luckperms.sponge.model;
import lombok.Getter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.LuckPermsSubjectData;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming;
@ -49,12 +50,10 @@ import org.spongepowered.api.service.permission.PermissionService;
import co.aikar.timings.Timing;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
public class SpongeUser extends User {
@Getter
private final UserSubject spongeData;
public SpongeUser(UUID uuid, LPSpongePlugin plugin) {
@ -67,6 +66,10 @@ public class SpongeUser extends User {
this.spongeData = new UserSubject(plugin, this);
}
public UserSubject sponge() {
return this.spongeData;
}
public static class UserSubject implements LPSubject {
private final SpongeUser parent;
private final LPSpongePlugin plugin;
@ -77,8 +80,6 @@ public class SpongeUser extends User {
@Getter
private final LuckPermsSubjectData transientSubjectData;
private long lastUse = System.currentTimeMillis();
private UserSubject(LPSpongePlugin plugin, SpongeUser parent) {
this.parent = parent;
this.plugin = plugin;
@ -86,16 +87,6 @@ public class SpongeUser extends User {
this.transientSubjectData = new LuckPermsSubjectData(false, plugin.getService(), parent, this);
}
private void logUsage() {
lastUse = System.currentTimeMillis();
}
public boolean shouldCleanup() {
long now = System.currentTimeMillis();
// Expire after 10 minutes of idle
return (now - lastUse) > 600000;
}
@Override
public String getIdentifier() {
return plugin.getUuidCache().getExternalUUID(parent.getUuid()).toString();
@ -114,8 +105,8 @@ public class SpongeUser extends User {
}
@Override
public SubjectCollectionReference getParentCollection() {
return plugin.getService().getUserSubjects().toReference();
public LPSubjectCollection getParentCollection() {
return plugin.getService().getUserSubjects();
}
@Override
@ -124,24 +115,21 @@ public class SpongeUser extends User {
}
@Override
public Tristate getPermissionValue(ContextSet contexts, String permission) {
logUsage();
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PERMISSION_VALUE)) {
return parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission);
}
}
@Override
public boolean isChildOf(ContextSet contexts, SubjectReference parent) {
logUsage();
public boolean isChildOf(ImmutableContextSet contexts, SubjectReference parent) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_IS_CHILD_OF)) {
return parent.getCollection().equals(PermissionService.SUBJECTS_GROUP) && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean();
}
}
@Override
public Set<SubjectReference> getParents(ContextSet contexts) {
logUsage();
public ImmutableList<SubjectReference> getParents(ImmutableContextSet contexts) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PARENTS)) {
ImmutableSet.Builder<SubjectReference> subjects = ImmutableSet.builder();
@ -152,20 +140,19 @@ public class SpongeUser extends User {
String groupName = perm.substring("group.".length());
if (plugin.getGroupManager().isLoaded(groupName)) {
subjects.add(plugin.getService().getGroupSubjects().get(groupName).toReference());
subjects.add(plugin.getService().getGroupSubjects().loadSubject(groupName).join().toReference());
}
}
subjects.addAll(plugin.getService().getUserSubjects().getDefaultSubject().resolve(getService()).getParents(contexts));
subjects.addAll(plugin.getService().getUserSubjects().getDefaults().getParents(contexts));
subjects.addAll(plugin.getService().getDefaults().getParents(contexts));
return subjects.build();
return getService().sortSubjects(subjects.build());
}
}
@Override
public Optional<String> getOption(ContextSet contexts, String s) {
logUsage();
public Optional<String> getOption(ImmutableContextSet contexts, String s) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_OPTION)) {
MetaData data = parent.getUserData().getMetaData(plugin.getService().calculateContexts(contexts));
if (s.equalsIgnoreCase("prefix")) {
@ -184,7 +171,7 @@ public class SpongeUser extends User {
return Optional.of(data.getMeta().get(s));
}
Optional<String> v = plugin.getService().getUserSubjects().getDefaultSubject().resolve(getService()).getOption(contexts, s);
Optional<String> v = plugin.getService().getUserSubjects().getDefaults().getOption(contexts, s);
if (v.isPresent()) {
return v;
}
@ -194,10 +181,9 @@ public class SpongeUser extends User {
}
@Override
public ContextSet getActiveContextSet() {
logUsage();
public ImmutableContextSet getActiveContextSet() {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_ACTIVE_CONTEXTS)) {
return plugin.getContextManager().getApplicableContext(this);
return plugin.getContextManager().getApplicableContext(this.sponge()).makeImmutable();
}
}
}

View File

@ -26,12 +26,8 @@
package me.lucko.luckperms.sponge.service;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
@ -43,37 +39,35 @@ import com.google.common.collect.MapMaker;
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.ContextCalculator;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.caching.UserCache;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.contexts.SpongeCalculatorLink;
import me.lucko.luckperms.sponge.managers.SpongeGroupManager;
import me.lucko.luckperms.sponge.managers.SpongeUserManager;
import me.lucko.luckperms.sponge.model.SpongeGroup;
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.description.SimpleDescriptionBuilder;
import me.lucko.luckperms.sponge.service.legacystorage.LegacyDataMigrator;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.persisted.PersistedCollection;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.proxy.PermissionServiceProxy;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.service.storage.SubjectStorage;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.service.context.ContextCalculator;
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 co.aikar.timings.Timing;
@ -81,31 +75,32 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
/**
* The LuckPerms implementation of the Sponge Permission Service
*/
@Getter
public class LuckPermsService implements PermissionService {
public class LuckPermsService implements LPPermissionService {
public static final String SERVER_CONTEXT = "server";
private final LPSpongePlugin plugin;
@Getter(AccessLevel.NONE)
private final PermissionServiceProxy spongeProxy;
private final SubjectStorage storage;
private final SpongeUserManager userSubjects;
private final PersistedCollection fallbackUserSubjects;
private final SpongeGroupManager groupSubjects;
private final PersistedCollection fallbackGroupSubjects;
private final PersistedCollection defaultSubjects;
private final Set<PermissionDescription> descriptionSet;
private final Set<LoadingCache<PermissionLookup, Tristate>> localPermissionCaches;
private final Set<LoadingCache<ImmutableContextSet, Set<SubjectReference>>> localParentCaches;
private final Set<LoadingCache<ImmutableContextSet, ImmutableList<SubjectReference>>> localParentCaches;
private final Set<LoadingCache<OptionLookup, Optional<String>>> localOptionCaches;
private final Set<CalculatedSubjectData> localDataCaches;
@ -114,7 +109,7 @@ public class LuckPermsService implements PermissionService {
.build(new CacheLoader<String, LPSubjectCollection>() {
@Override
public LPSubjectCollection load(String s) {
return new PersistedCollection(LuckPermsService.this, s, true);
return new PersistedCollection(LuckPermsService.this, s);
}
@Override
@ -125,26 +120,23 @@ public class LuckPermsService implements PermissionService {
public LuckPermsService(LPSpongePlugin plugin) {
this.plugin = plugin;
this.spongeProxy = new PermissionServiceProxy(this);
localPermissionCaches = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
localParentCaches = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
localOptionCaches = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
localDataCaches = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
storage = new SubjectStorage(new File(plugin.getDataDirectory(), "sponge-data"));
storage = new SubjectStorage(this, new File(plugin.getDataDirectory(), "sponge-data"));
new LegacyDataMigrator(plugin, new File(plugin.getDataDirectory(), "local"), storage).run();
userSubjects = plugin.getUserManager();
fallbackUserSubjects = new PersistedCollection(this, "fallback-users", true);
groupSubjects = plugin.getGroupManager();
fallbackGroupSubjects = new PersistedCollection(this, "fallback-groups", true);
defaultSubjects = new PersistedCollection(this, "defaults", false);
defaultSubjects = new PersistedCollection(this, "defaults");
defaultSubjects.loadAll();
collections.put(PermissionService.SUBJECTS_USER, userSubjects);
collections.put("fallback-users", fallbackUserSubjects);
collections.put(PermissionService.SUBJECTS_GROUP, groupSubjects);
collections.put("fallback-groups", fallbackGroupSubjects);
collections.put("defaults", defaultSubjects);
for (String collection : storage.getSavedCollections()) {
@ -152,7 +144,7 @@ public class LuckPermsService implements PermissionService {
continue;
}
PersistedCollection c = new PersistedCollection(this, collection.toLowerCase(), true);
PersistedCollection c = new PersistedCollection(this, collection.toLowerCase());
c.loadAll();
collections.put(c.getIdentifier(), c);
}
@ -160,40 +152,46 @@ public class LuckPermsService implements PermissionService {
descriptionSet = ConcurrentHashMap.newKeySet();
}
public LPSubjectData getDefaultData() {
return getDefaults().getSubjectData();
@Override
public PermissionService sponge() {
return spongeProxy;
}
@Override
public LPSubject getDefaults() {
return getDefaultSubjects().get("default");
return getDefaultSubjects().loadSubject("default").join();
}
@Override
public LPSubjectCollection getSubjects(String s) {
public Predicate<String> getIdentifierValidityPredicate() {
return Predicates.alwaysTrue();
}
@Override
public LPSubjectCollection getCollection(String s) {
try (Timing ignored = plugin.getTimings().time(LPTiming.GET_SUBJECTS)) {
return collections.get(s.toLowerCase());
}
}
public Map<String, LPSubjectCollection> getCollections() {
@Override
public ImmutableMap<String, LPSubjectCollection> getLoadedCollections() {
return ImmutableMap.copyOf(collections.asMap());
}
@Deprecated
@Override
public Map<String, SubjectCollection> getKnownSubjects() {
return getCollections().entrySet().stream().collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
public SubjectReference newSubjectReference(String collectionIdentifier, String subjectIdentifier) {
return SubjectReference.of(this, collectionIdentifier, subjectIdentifier);
}
@Override
public Optional<PermissionDescription.Builder> newDescriptionBuilder(@NonNull Object o) {
public PermissionDescription.Builder newDescriptionBuilder(@NonNull Object o) {
Optional<PluginContainer> container = plugin.getGame().getPluginManager().fromInstance(o);
if (!container.isPresent()) {
throw new IllegalArgumentException("Couldn't find a plugin container for " + o.getClass().getSimpleName());
}
return Optional.of(new DescriptionBuilder(this, container.get()));
return new SimpleDescriptionBuilder(this, container.get());
}
@Override
@ -208,24 +206,25 @@ public class LuckPermsService implements PermissionService {
}
@Override
public Collection<PermissionDescription> getDescriptions() {
public ImmutableSet<PermissionDescription> getDescriptions() {
return ImmutableSet.copyOf(descriptionSet);
}
@Override
public void registerContextCalculator(@NonNull ContextCalculator<Subject> contextCalculator) {
plugin.getContextManager().registerCalculator(new SpongeCalculatorLink(contextCalculator));
plugin.getContextManager().registerCalculator(contextCalculator);
}
public List<Subject> sortSubjects(List<Subject> s) {
List<Subject> ret = new ArrayList<>(s);
@Override
public ImmutableList<SubjectReference> sortSubjects(Collection<SubjectReference> s) {
List<SubjectReference> ret = new ArrayList<>(s);
ret.sort(Collections.reverseOrder((o1, o2) -> {
if (o1.equals(o2)) {
return 0;
}
boolean o1isGroup = o1.getContainingCollection().getIdentifier().equals(PermissionService.SUBJECTS_GROUP);
boolean o2isGroup = o2.getContainingCollection().getIdentifier().equals(PermissionService.SUBJECTS_GROUP);
boolean o1isGroup = o1.getCollection().equals(PermissionService.SUBJECTS_GROUP);
boolean o2isGroup = o2.getCollection().equals(PermissionService.SUBJECTS_GROUP);
if (o1isGroup != o2isGroup) {
return o1isGroup ? 1 : -1;
@ -256,7 +255,8 @@ public class LuckPermsService implements PermissionService {
return ImmutableList.copyOf(ret);
}
public Contexts calculateContexts(ContextSet contextSet) {
@Override
public Contexts calculateContexts(ImmutableContextSet contextSet) {
return new Contexts(
contextSet,
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
@ -268,6 +268,7 @@ public class LuckPermsService implements PermissionService {
);
}
@Override
public void invalidatePermissionCaches() {
for (LoadingCache<PermissionLookup, Tristate> c : localPermissionCaches) {
c.invalidateAll();
@ -284,18 +285,20 @@ public class LuckPermsService implements PermissionService {
}
for (SpongeGroup group : plugin.getGroupManager().getAll().values()) {
group.getSpongeData().invalidateCaches();
group.sponge().invalidateCaches();
}
}
@Override
public void invalidateParentCaches() {
for (LoadingCache<ImmutableContextSet, Set<SubjectReference>> c : localParentCaches) {
for (LoadingCache<ImmutableContextSet, ImmutableList<SubjectReference>> c : localParentCaches) {
c.invalidateAll();
}
invalidateOptionCaches();
invalidatePermissionCaches();
}
@Override
public void invalidateOptionCaches() {
for (LoadingCache<OptionLookup, Optional<String>> c : localOptionCaches) {
c.invalidateAll();
@ -306,74 +309,4 @@ public class LuckPermsService implements PermissionService {
userCache.invalidateCache();
}
}
@RequiredArgsConstructor
@EqualsAndHashCode
@ToString
public static final class DescriptionBuilder implements PermissionDescription.Builder {
private final LuckPermsService service;
private final PluginContainer container;
private final Map<String, Tristate> roles = new HashMap<>();
private String id = null;
private Text description = null;
@Override
public PermissionDescription.Builder id(@NonNull String s) {
id = s;
return this;
}
@Override
public PermissionDescription.Builder description(@NonNull Text text) {
description = text;
return this;
}
@Override
public PermissionDescription.Builder assign(@NonNull String s, boolean b) {
roles.put(s, Tristate.fromBoolean(b));
return this;
}
@Override
public PermissionDescription register() throws IllegalStateException {
if (id == null) {
throw new IllegalStateException("id cannot be null");
}
if (description == null) {
throw new IllegalStateException("description cannot be null");
}
Description d = new Description(service, container, id, description);
service.getDescriptionSet().add(d);
// Set role-templates
LPSubjectCollection subjects = service.getSubjects(PermissionService.SUBJECTS_ROLE_TEMPLATE);
for (Map.Entry<String, Tristate> assignment : roles.entrySet()) {
LPSubject subject = subjects.get(assignment.getKey());
subject.getTransientSubjectData().setPermission(ContextSet.empty(), id, assignment.getValue());
}
service.getPlugin().getPermissionVault().offer(id);
return d;
}
}
@Getter
@AllArgsConstructor
@EqualsAndHashCode
@ToString
public static final class Description implements PermissionDescription {
private final LuckPermsService service;
private final PluginContainer owner;
private final String id;
private final Text description;
@Override
public Map<Subject, Boolean> getAssignedSubjects(String id) {
SubjectCollection subjects = service.getSubjects(id);
return subjects.getAllWithPermission(this.id);
}
}
}

View File

@ -29,13 +29,12 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.DataMutateResult;
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.ImmutableContextSet;
import me.lucko.luckperms.common.caching.MetaAccumulator;
import me.lucko.luckperms.common.core.NodeFactory;
@ -43,8 +42,8 @@ import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.timings.LPTiming;
@ -56,7 +55,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -74,7 +73,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
LPSubject parentSubject;
@Override
public Map<ImmutableContextSet, Map<String, Boolean>> getPermissions() {
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) {
Map<ImmutableContextSet, ImmutableMap.Builder<String, Boolean>> perms = new HashMap<>();
@ -86,7 +85,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
perms.put(e.getKey(), results);
}
ImmutableMap.Builder<ImmutableContextSet, Map<String, Boolean>> map = ImmutableMap.builder();
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, Boolean>> map = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, ImmutableMap.Builder<String, Boolean>> e : perms.entrySet()) {
map.put(e.getKey(), e.getValue().build());
}
@ -95,7 +94,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
}
@Override
public boolean setPermission(@NonNull ContextSet contexts, @NonNull String permission, @NonNull Tristate tristate) {
public CompletableFuture<Boolean> setPermission(@NonNull ImmutableContextSet contexts, @NonNull String permission, @NonNull Tristate tristate) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_PERMISSION)) {
if (tristate == Tristate.UNDEFINED) {
// Unset
@ -107,8 +106,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
holder.unsetTransientPermission(node);
}
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
}
Node node = NodeFactory.newBuilder(permission).setValue(tristate.asBoolean()).withExtraContext(contexts).build();
@ -126,13 +124,12 @@ public class LuckPermsSubjectData implements LPSubjectData {
holder.setTransientPermission(node);
}
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
}
}
@Override
public boolean clearPermissions() {
public CompletableFuture<Boolean> clearPermissions() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) {
boolean ret;
if (enduring) {
@ -142,20 +139,19 @@ public class LuckPermsSubjectData implements LPSubjectData {
}
if (!ret) {
return false;
return CompletableFuture.completedFuture(false);
}
if (holder instanceof User) {
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
}
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
}
}
@Override
public boolean clearPermissions(@NonNull ContextSet set) {
public CompletableFuture<Boolean> clearPermissions(@NonNull ImmutableContextSet set) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) {
boolean ret;
@ -171,35 +167,34 @@ public class LuckPermsSubjectData implements LPSubjectData {
}
if (!ret) {
return false;
return CompletableFuture.completedFuture(false);
}
if (holder instanceof User) {
service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false);
}
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
}
}
@Override
public Map<ImmutableContextSet, Set<SubjectReference>> getParents() {
public ImmutableMap<ImmutableContextSet, ImmutableList<SubjectReference>> getAllParents() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PARENTS)) {
Map<ImmutableContextSet, ImmutableSet.Builder<SubjectReference>> parents = new HashMap<>();
Map<ImmutableContextSet, ImmutableList.Builder<SubjectReference>> parents = new HashMap<>();
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getNodes() : holder.getTransientNodes()).asMap().entrySet()) {
ImmutableSet.Builder<SubjectReference> results = ImmutableSet.builder();
ImmutableList.Builder<SubjectReference> results = ImmutableList.builder();
for (Node n : e.getValue()) {
if (n.isGroupNode()) {
results.add(service.getGroupSubjects().get(n.getGroupName()).toReference());
results.add(service.getGroupSubjects().loadSubject(n.getGroupName()).join().toReference());
}
}
parents.put(e.getKey(), results);
}
ImmutableMap.Builder<ImmutableContextSet, Set<SubjectReference>> map = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, ImmutableSet.Builder<SubjectReference>> e : parents.entrySet()) {
ImmutableMap.Builder<ImmutableContextSet, ImmutableList<SubjectReference>> map = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, ImmutableList.Builder<SubjectReference>> e : parents.entrySet()) {
map.put(e.getKey(), e.getValue().build());
}
return map.build();
@ -207,63 +202,63 @@ public class LuckPermsSubjectData implements LPSubjectData {
}
@Override
public boolean addParent(@NonNull ContextSet contexts, @NonNull SubjectReference subject) {
public CompletableFuture<Boolean> addParent(@NonNull ImmutableContextSet contexts, @NonNull SubjectReference subject) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_ADD_PARENT)) {
if (subject.getCollection().equals(PermissionService.SUBJECTS_GROUP)) {
LPSubject permsSubject = subject.resolve(service);
DataMutateResult result;
return subject.resolve().thenCompose(sub -> {
DataMutateResult result;
if (enduring) {
result = holder.setPermission(NodeFactory.newBuilder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
} else {
result = holder.setTransientPermission(NodeFactory.newBuilder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
}
if (enduring) {
result = holder.setPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
.withExtraContext(contexts)
.build());
} else {
result = holder.setTransientPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
.withExtraContext(contexts)
.build());
}
if (!result.asBoolean()) {
return false;
}
if (!result.asBoolean()) {
return CompletableFuture.completedFuture(false);
}
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
});
}
return false;
return CompletableFuture.completedFuture(false);
}
}
@Override
public boolean removeParent(@NonNull ContextSet contexts, @NonNull SubjectReference subject) {
public CompletableFuture<Boolean> removeParent(@NonNull ImmutableContextSet contexts, @NonNull SubjectReference subject) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_REMOVE_PARENT)) {
if (subject.getCollection().equals(PermissionService.SUBJECTS_GROUP)) {
LPSubject permsSubject = subject.resolve(service);
DataMutateResult result;
subject.resolve().thenCompose(sub -> {
DataMutateResult result;
if (enduring) {
result = holder.unsetPermission(NodeFactory.newBuilder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
} else {
result = holder.unsetTransientPermission(NodeFactory.newBuilder("group." + permsSubject.getIdentifier())
.withExtraContext(contexts)
.build());
}
if (enduring) {
result = holder.unsetPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
.withExtraContext(contexts)
.build());
} else {
result = holder.unsetTransientPermission(NodeFactory.newBuilder("group." + sub.getIdentifier())
.withExtraContext(contexts)
.build());
}
if (!result.asBoolean()) {
return false;
}
if (!result.asBoolean()) {
return CompletableFuture.completedFuture(false);
}
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
});
}
return false;
return CompletableFuture.completedFuture(false);
}
}
@Override
public boolean clearParents() {
public CompletableFuture<Boolean> clearParents() {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PARENTS)) {
boolean ret;
@ -283,16 +278,15 @@ public class LuckPermsSubjectData implements LPSubjectData {
}
if (!ret) {
return false;
return CompletableFuture.completedFuture(false);
}
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
}
}
@Override
public boolean clearParents(@NonNull ContextSet set) {
public CompletableFuture<Boolean> clearParents(@NonNull ImmutableContextSet set) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PARENTS)) {
boolean ret;
if (enduring) {
@ -312,16 +306,15 @@ public class LuckPermsSubjectData implements LPSubjectData {
}
if (!ret) {
return false;
return CompletableFuture.completedFuture(false);
}
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
}
}
@Override
public Map<ImmutableContextSet, Map<String, String>> getOptions() {
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, String>> getAllOptions() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_OPTIONS)) {
Map<ImmutableContextSet, Map<String, String>> options = new HashMap<>();
Map<ImmutableContextSet, Integer> minPrefixPriority = new HashMap<>();
@ -363,7 +356,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
}
}
ImmutableMap.Builder<ImmutableContextSet, Map<String, String>> map = ImmutableMap.builder();
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, String>> map = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Map<String, String>> e : options.entrySet()) {
map.put(e.getKey(), ImmutableMap.copyOf(e.getValue()));
}
@ -372,7 +365,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
}
@Override
public boolean setOption(@NonNull ContextSet context, @NonNull String key, @NonNull String value) {
public CompletableFuture<Boolean> setOption(@NonNull ImmutableContextSet context, @NonNull String key, @NonNull String value) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_OPTION)) {
if (key.equalsIgnoreCase("prefix") || key.equalsIgnoreCase("suffix")) {
// special handling.
@ -414,13 +407,12 @@ public class LuckPermsSubjectData implements LPSubjectData {
}
}
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
}
}
@Override
public boolean unsetOption(ContextSet set, String key) {
public CompletableFuture<Boolean> unsetOption(ImmutableContextSet set, String key) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_OPTION)) {
List<Node> toRemove = streamNodes(enduring)
.filter(n -> {
@ -437,13 +429,12 @@ public class LuckPermsSubjectData implements LPSubjectData {
toRemove.forEach(makeUnsetConsumer(enduring));
objectSave(holder);
return true;
return objectSave(holder).thenApply(v -> true);
}
}
@Override
public boolean clearOptions(@NonNull ContextSet set) {
public CompletableFuture<Boolean> clearOptions(@NonNull ImmutableContextSet set) {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_OPTIONS)) {
List<Node> toRemove = streamNodes(enduring)
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
@ -452,13 +443,12 @@ public class LuckPermsSubjectData implements LPSubjectData {
toRemove.forEach(makeUnsetConsumer(enduring));
objectSave(holder);
return !toRemove.isEmpty();
return objectSave(holder).thenApply(v -> !toRemove.isEmpty());
}
}
@Override
public boolean clearOptions() {
public CompletableFuture<Boolean> clearOptions() {
try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_OPTIONS)) {
List<Node> toRemove = streamNodes(enduring)
.filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix())
@ -466,8 +456,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
toRemove.forEach(makeUnsetConsumer(enduring));
objectSave(holder);
return !toRemove.isEmpty();
return objectSave(holder).thenApply(v -> !toRemove.isEmpty());
}
}
@ -485,21 +474,22 @@ public class LuckPermsSubjectData implements LPSubjectData {
};
}
private void objectSave(PermissionHolder t) {
private CompletableFuture<Void> objectSave(PermissionHolder t) {
if (!enduring) {
// don't bother saving to primary storage. just refresh
if (t instanceof User) {
((User) t).getRefreshBuffer().request();
User user = ((User) t);
return user.getRefreshBuffer().request();
} else {
service.getPlugin().getUpdateTaskBuffer().request();
return service.getPlugin().getUpdateTaskBuffer().request();
}
} else {
if (t instanceof User) {
service.getPlugin().getStorage().saveUser(((User) t))
.thenRunAsync(() -> ((User) t).getRefreshBuffer().request(), service.getPlugin().getScheduler().getAsyncExecutor());
User user = ((User) t);
return service.getPlugin().getStorage().saveUser(user).thenCombineAsync(user.getRefreshBuffer().request(), (b, v) -> v, service.getPlugin().getScheduler().getAsyncExecutor());
} else {
service.getPlugin().getStorage().saveGroup((Group) t)
.thenRunAsync(() -> service.getPlugin().getUpdateTaskBuffer().request(), service.getPlugin().getScheduler().getAsyncExecutor());
Group group = ((Group) t);
return service.getPlugin().getStorage().saveGroup(group).thenCombineAsync(service.getPlugin().getUpdateTaskBuffer().request(), (b, v) -> v, service.getPlugin().getScheduler().getAsyncExecutor());
}
}
}

View File

@ -27,8 +27,8 @@ package me.lucko.luckperms.sponge.service;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
@RequiredArgsConstructor
public class ServiceCacheHousekeepingTask implements Runnable {
@ -36,8 +36,8 @@ public class ServiceCacheHousekeepingTask implements Runnable {
@Override
public void run() {
for (LPSubjectCollection collection : service.getCollections().values()) {
for (LPSubject subject : collection.getSubjects()) {
for (LPSubjectCollection collection : service.getLoadedCollections().values()) {
for (LPSubject subject : collection.getLoadedSubjects()) {
subject.performCleanup();
}
}

View File

@ -33,7 +33,6 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import me.lucko.luckperms.api.Tristate;
@ -43,9 +42,9 @@ import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.calculators.PermissionProcessor;
import me.lucko.luckperms.common.calculators.processors.MapProcessor;
import me.lucko.luckperms.sponge.calculators.SpongeWildcardProcessor;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectData;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import java.util.Comparator;
@ -55,9 +54,13 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* In-memory implementation of {@link LPSubjectData}.
*/
@RequiredArgsConstructor
public class CalculatedSubjectData implements LPSubjectData {
private static final ContextComparator CONTEXT_COMPARATOR = new ContextComparator();
@ -65,10 +68,13 @@ public class CalculatedSubjectData implements LPSubjectData {
@Getter
private final LPSubject parentSubject;
private final LuckPermsService service;
private final LPPermissionService service;
private final String calculatorDisplayName;
private final Map<ContextSet, Map<String, Boolean>> permissions = new ConcurrentHashMap<>();
private final Map<ImmutableContextSet, Map<String, Boolean>> permissions = new ConcurrentHashMap<>();
private final Map<ImmutableContextSet, Set<SubjectReference>> parents = new ConcurrentHashMap<>();
private final Map<ImmutableContextSet, Map<String, String>> options = new ConcurrentHashMap<>();
private final LoadingCache<ContextSet, CalculatorHolder> permissionCache = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(new CacheLoader<ContextSet, CalculatorHolder>() {
@ -85,9 +91,6 @@ public class CalculatedSubjectData implements LPSubjectData {
}
});
private final Map<ContextSet, Set<SubjectReference>> parents = new ConcurrentHashMap<>();
private final Map<ContextSet, Map<String, String>> options = new ConcurrentHashMap<>();
public void cleanup() {
permissionCache.cleanUp();
}
@ -128,210 +131,185 @@ public class CalculatedSubjectData implements LPSubjectData {
}
@Override
public Map<ImmutableContextSet, Map<String, Boolean>> getPermissions() {
ImmutableMap.Builder<ImmutableContextSet, Map<String, Boolean>> map = ImmutableMap.builder();
for (Map.Entry<ContextSet, Map<String, Boolean>> e : permissions.entrySet()) {
map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions() {
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, Boolean>> map = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Map<String, Boolean>> e : permissions.entrySet()) {
map.put(e.getKey(), ImmutableMap.copyOf(e.getValue()));
}
return map.build();
}
@Override
public Map<String, Boolean> getPermissions(ContextSet contexts) {
return ImmutableMap.copyOf(permissions.getOrDefault(contexts, ImmutableMap.of()));
}
@Override
public boolean setPermission(ContextSet contexts, String permission, Tristate value) {
public CompletableFuture<Boolean> setPermission(ImmutableContextSet contexts, String permission, Tristate value) {
boolean b;
if (value == Tristate.UNDEFINED) {
Map<String, Boolean> perms = permissions.get(contexts);
b = perms != null && perms.remove(permission.toLowerCase()) != null;
} else {
Map<String, Boolean> perms = permissions.computeIfAbsent(contexts.makeImmutable(), c -> new ConcurrentHashMap<>());
Map<String, Boolean> perms = permissions.computeIfAbsent(contexts, c -> new ConcurrentHashMap<>());
b = !Objects.equals(perms.put(permission.toLowerCase(), value.asBoolean()), value.asBoolean());
}
if (b) {
permissionCache.invalidateAll();
service.invalidatePermissionCaches();
}
return b;
return CompletableFuture.completedFuture(b);
}
@Override
public boolean clearPermissions() {
public CompletableFuture<Boolean> clearPermissions() {
if (permissions.isEmpty()) {
return false;
return CompletableFuture.completedFuture(false);
} else {
permissions.clear();
permissionCache.invalidateAll();
service.invalidatePermissionCaches();
return true;
return CompletableFuture.completedFuture(true);
}
}
@Override
public boolean clearPermissions(ContextSet contexts) {
public CompletableFuture<Boolean> clearPermissions(ImmutableContextSet contexts) {
Map<String, Boolean> perms = permissions.get(contexts);
if (perms == null) {
return false;
return CompletableFuture.completedFuture(false);
}
permissions.remove(contexts);
if (!perms.isEmpty()) {
permissionCache.invalidateAll();
service.invalidatePermissionCaches();
return true;
return CompletableFuture.completedFuture(true);
}
return false;
return CompletableFuture.completedFuture(false);
}
@Override
public Map<ImmutableContextSet, Set<SubjectReference>> getParents() {
ImmutableMap.Builder<ImmutableContextSet, Set<SubjectReference>> map = ImmutableMap.builder();
for (Map.Entry<ContextSet, Set<SubjectReference>> e : parents.entrySet()) {
map.put(e.getKey().makeImmutable(), ImmutableSet.copyOf(e.getValue()));
}
return map.build();
}
public Map<ImmutableContextSet, List<SubjectReference>> getParentsAsList() {
ImmutableMap.Builder<ImmutableContextSet, List<SubjectReference>> map = ImmutableMap.builder();
for (Map.Entry<ContextSet, Set<SubjectReference>> e : parents.entrySet()) {
map.put(e.getKey().makeImmutable(), ImmutableList.copyOf(e.getValue()));
public ImmutableMap<ImmutableContextSet, ImmutableList<SubjectReference>> getAllParents() {
ImmutableMap.Builder<ImmutableContextSet, ImmutableList<SubjectReference>> map = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Set<SubjectReference>> e : parents.entrySet()) {
map.put(e.getKey(), service.sortSubjects(e.getValue()));
}
return map.build();
}
@Override
public Set<SubjectReference> getParents(ContextSet contexts) {
return ImmutableSet.copyOf(parents.getOrDefault(contexts, ImmutableSet.of()));
}
@Override
public boolean addParent(ContextSet contexts, SubjectReference parent) {
Set<SubjectReference> set = parents.computeIfAbsent(contexts.makeImmutable(), c -> ConcurrentHashMap.newKeySet());
public CompletableFuture<Boolean> addParent(ImmutableContextSet contexts, SubjectReference parent) {
Set<SubjectReference> set = parents.computeIfAbsent(contexts, c -> ConcurrentHashMap.newKeySet());
boolean b = set.add(parent);
if (b) {
service.invalidateParentCaches();
}
return b;
return CompletableFuture.completedFuture(b);
}
@Override
public boolean removeParent(ContextSet contexts, SubjectReference parent) {
public CompletableFuture<Boolean> removeParent(ImmutableContextSet contexts, SubjectReference parent) {
Set<SubjectReference> set = parents.get(contexts);
boolean b = set != null && set.remove(parent);
if (b) {
service.invalidateParentCaches();
}
return b;
return CompletableFuture.completedFuture(b);
}
@Override
public boolean clearParents() {
public CompletableFuture<Boolean> clearParents() {
if (parents.isEmpty()) {
return false;
return CompletableFuture.completedFuture(false);
} else {
parents.clear();
service.invalidateOptionCaches();
return true;
return CompletableFuture.completedFuture(true);
}
}
@Override
public boolean clearParents(ContextSet contexts) {
public CompletableFuture<Boolean> clearParents(ImmutableContextSet contexts) {
Set<SubjectReference> set = parents.get(contexts);
if (set == null) {
return false;
return CompletableFuture.completedFuture(false);
}
parents.remove(contexts);
service.invalidateParentCaches();
return !set.isEmpty();
return CompletableFuture.completedFuture(!set.isEmpty());
}
@Override
public Map<ImmutableContextSet, Map<String, String>> getOptions() {
ImmutableMap.Builder<ImmutableContextSet, Map<String, String>> map = ImmutableMap.builder();
for (Map.Entry<ContextSet, Map<String, String>> e : options.entrySet()) {
map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, String>> getAllOptions() {
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, String>> map = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Map<String, String>> e : options.entrySet()) {
map.put(e.getKey(), ImmutableMap.copyOf(e.getValue()));
}
return map.build();
}
@Override
public Map<String, String> getOptions(ContextSet contexts) {
return ImmutableMap.copyOf(options.getOrDefault(contexts, ImmutableMap.of()));
}
@Override
public boolean setOption(ContextSet contexts, String key, String value) {
Map<String, String> options = this.options.computeIfAbsent(contexts.makeImmutable(), c -> new ConcurrentHashMap<>());
public CompletableFuture<Boolean> setOption(ImmutableContextSet contexts, String key, String value) {
Map<String, String> options = this.options.computeIfAbsent(contexts, c -> new ConcurrentHashMap<>());
boolean b = !stringEquals(options.put(key.toLowerCase(), value), value);
if (b) {
service.invalidateOptionCaches();
}
return b;
return CompletableFuture.completedFuture(b);
}
@Override
public boolean unsetOption(ContextSet contexts, String key) {
public CompletableFuture<Boolean> unsetOption(ImmutableContextSet contexts, String key) {
Map<String, String> options = this.options.get(contexts);
boolean b = options != null && options.remove(key.toLowerCase()) != null;
if (b) {
service.invalidateOptionCaches();
}
return b;
return CompletableFuture.completedFuture(b);
}
@Override
public boolean clearOptions() {
public CompletableFuture<Boolean> clearOptions() {
if (options.isEmpty()) {
return false;
return CompletableFuture.completedFuture(false);
} else {
options.clear();
service.invalidateOptionCaches();
return true;
return CompletableFuture.completedFuture(true);
}
}
@Override
public boolean clearOptions(ContextSet contexts) {
public CompletableFuture<Boolean> clearOptions(ImmutableContextSet contexts) {
Map<String, String> map = options.get(contexts);
if (map == null) {
return false;
return CompletableFuture.completedFuture(false);
}
options.remove(contexts);
service.invalidateOptionCaches();
return !map.isEmpty();
return CompletableFuture.completedFuture(!map.isEmpty());
}
private static <V> Map<String, V> flattenMap(ContextSet contexts, Map<ContextSet, Map<String, V>> source) {
private static <V> Map<String, V> flattenMap(ContextSet contexts, Map<ImmutableContextSet, Map<String, V>> source) {
Map<String, V> map = new HashMap<>();
SortedMap<ContextSet, Map<String, V>> ret = getRelevantEntries(contexts, source);
SortedMap<ImmutableContextSet, Map<String, V>> ret = getRelevantEntries(contexts, source);
for (Map<String, V> m : ret.values()) {
for (Map.Entry<String, V> e : m.entrySet()) {
if (!map.containsKey(e.getKey())) {
map.put(e.getKey(), e.getValue());
}
map.putIfAbsent(e.getKey(), e.getValue());
}
}
return ImmutableMap.copyOf(map);
}
private static <K, V> SortedMap<ContextSet, Map<K, V>> getRelevantEntries(ContextSet set, Map<ContextSet, Map<K, V>> map) {
ImmutableSortedMap.Builder<ContextSet, Map<K, V>> perms = ImmutableSortedMap.orderedBy(CONTEXT_COMPARATOR);
private static <K, V> SortedMap<ImmutableContextSet, Map<K, V>> getRelevantEntries(ContextSet set, Map<ImmutableContextSet, Map<K, V>> map) {
ImmutableSortedMap.Builder<ImmutableContextSet, Map<K, V>> perms = ImmutableSortedMap.orderedBy(CONTEXT_COMPARATOR);
for (Map.Entry<ContextSet, Map<K, V>> e : map.entrySet()) {
for (Map.Entry<ImmutableContextSet, Map<K, V>> e : map.entrySet()) {
if (!e.getKey().isSatisfiedBy(set)) {
continue;
}
perms.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
perms.put(e.getKey(), ImmutableMap.copyOf(e.getValue()));
}
return perms.build();
@ -341,10 +319,10 @@ public class CalculatedSubjectData implements LPSubjectData {
return a == null && b == null || a != null && b != null && a.equalsIgnoreCase(b);
}
private static class ContextComparator implements Comparator<ContextSet> {
private static class ContextComparator implements Comparator<ImmutableContextSet> {
@Override
public int compare(ContextSet o1, ContextSet o2) {
public int compare(ImmutableContextSet o1, ImmutableContextSet o2) {
int i = Integer.compare(o1.size(), o2.size());
return i == 0 ? 1 : i;
}

View File

@ -0,0 +1,70 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.description;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.proxy.SubjectProxy;
import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.service.permission.PermissionDescription;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.text.Text;
import java.util.Map;
@SuppressWarnings("unchecked")
@Getter
@AllArgsConstructor
@EqualsAndHashCode(of = {"owner", "id", "description"})
@ToString(of = {"owner", "id", "description"})
public final class SimpleDescription implements PermissionDescription {
@Getter(AccessLevel.NONE)
private final LPPermissionService service;
private final PluginContainer owner;
private final String id;
private final Text description;
@Override
public Map<Subject, Boolean> getAssignedSubjects(String id) {
LPSubjectCollection subjects = service.getCollection(id);
return (Map) subjects.getAllWithPermission(this.id)
.thenApply(map -> map.entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
e -> new SubjectProxy(service, e.getKey()),
Map.Entry::getValue)
)
).join();
}
}

View File

@ -0,0 +1,103 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.description;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
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.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.service.permission.PermissionDescription;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.text.Text;
import java.util.HashMap;
import java.util.Map;
@ToString(of = {"container", "roles", "id", "description"})
@EqualsAndHashCode(of = {"container", "roles", "id", "description"})
@RequiredArgsConstructor
public final class SimpleDescriptionBuilder implements PermissionDescription.Builder {
private final LuckPermsService service;
private final PluginContainer container;
private final Map<String, Tristate> roles = new HashMap<>();
private String id = null;
private Text description = null;
@Override
public PermissionDescription.Builder id(@NonNull String s) {
id = s;
return this;
}
@Override
public PermissionDescription.Builder description(@NonNull Text text) {
description = text;
return this;
}
@Override
public PermissionDescription.Builder assign(@NonNull String s, boolean b) {
roles.put(s, Tristate.fromBoolean(b));
return this;
}
@Override
public PermissionDescription register() throws IllegalStateException {
if (id == null) {
throw new IllegalStateException("id cannot be null");
}
if (description == null) {
throw new IllegalStateException("description cannot be null");
}
SimpleDescription d = new SimpleDescription(service, container, id, description);
service.getDescriptionSet().add(d);
// Set role-templates
LPSubjectCollection subjects = service.getCollection(PermissionService.SUBJECTS_ROLE_TEMPLATE);
for (Map.Entry<String, Tristate> assignment : roles.entrySet()) {
LPSubject subject = subjects.loadSubject(assignment.getKey()).join();
subject.getTransientSubjectData().setPermission(ContextSet.empty(), id, assignment.getValue());
}
service.getPlugin().getPermissionVault().offer(id);
// null stuff so this instance can be reused
roles.clear();
id = null;
description = null;
return d;
}
}

View File

@ -27,7 +27,7 @@ package me.lucko.luckperms.sponge.service.legacystorage;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.storage.SubjectStorage;
import java.io.BufferedReader;
@ -39,7 +39,7 @@ import java.nio.file.Files;
@SuppressWarnings("deprecation")
@RequiredArgsConstructor
public class LegacyDataMigrator implements Runnable {
private final LuckPermsPlugin plugin;
private final LPSpongePlugin plugin;
private final File oldDirectory;
private final SubjectStorage storage;
@ -69,7 +69,7 @@ public class LegacyDataMigrator implements Runnable {
try (BufferedReader reader = Files.newBufferedReader(subjectFile.toPath(), StandardCharsets.UTF_8)) {
SubjectDataHolder holder = storage.getGson().fromJson(reader, SubjectDataHolder.class);
storage.saveToFile(holder.asSubjectModel(), storage.resolveFile(collectionDir.getName(), subjectName));
storage.saveToFile(holder.asSubjectModel(plugin.getService()), storage.resolveFile(collectionDir.getName(), subjectName));
} catch (IOException e) {
e.printStackTrace();
}

View File

@ -27,7 +27,8 @@ package me.lucko.luckperms.sponge.service.legacystorage;
import lombok.ToString;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.service.storage.SubjectStorageModel;
@ -50,22 +51,22 @@ public class SubjectDataHolder {
// For gson
}
public SubjectStorageModel asSubjectModel() {
return new SubjectStorageModel(
public SubjectStorageModel asSubjectModel(LPPermissionService service) {
return new SubjectStorageModel(service,
permissions.entrySet().stream()
.collect(Collectors.toMap(
k -> ContextSet.fromMap(k.getKey()),
k -> ImmutableContextSet.fromMap(k.getKey()),
Map.Entry::getValue
)),
options.entrySet().stream()
.collect(Collectors.toMap(
k -> ContextSet.fromMap(k.getKey()),
k -> ImmutableContextSet.fromMap(k.getKey()),
Map.Entry::getValue
)),
parents.entrySet().stream()
.collect(Collectors.toMap(
k -> ContextSet.fromMap(k.getKey()),
v -> v.getValue().stream().map(SubjectReference::deserialize).collect(Collectors.toList())
k -> ImmutableContextSet.fromMap(k.getKey()),
v -> v.getValue().stream().map(s -> SubjectReference.deserialize(service, s)).collect(Collectors.toList())
))
);
}

View File

@ -23,7 +23,7 @@
* SOFTWARE.
*/
package me.lucko.luckperms.sponge.service.proxy;
package me.lucko.luckperms.sponge.service.model;
import lombok.NonNull;
import lombok.experimental.UtilityClass;
@ -41,19 +41,22 @@ import org.spongepowered.api.util.Tristate;
import java.util.Set;
/**
* Utility class for converting between Sponge and LuckPerms context and tristate classes
*/
@UtilityClass
public class Util {
public class CompatibilityUtil {
private static final LoadingCache<Set<Context>, ImmutableContextSet> SPONGE_TO_LP_CACHE = Caffeine.newBuilder()
.build(ImmutableContextSet::fromEntries);
private static final LoadingCache<ImmutableContextSet, Set<Context>> LP_TO_SPONGE_CACHE = Caffeine.newBuilder()
private static final LoadingCache<ImmutableContextSet, ImmutableSet<Context>> LP_TO_SPONGE_CACHE = Caffeine.newBuilder()
.build(set -> set.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(ImmutableCollectors.toImmutableSet()));
public static ContextSet convertContexts(@NonNull Set<Context> contexts) {
public static ImmutableContextSet convertContexts(@NonNull Set<Context> contexts) {
return SPONGE_TO_LP_CACHE.get(ImmutableSet.copyOf(contexts));
}
public static Set<Context> convertContexts(@NonNull ContextSet contexts) {
public static ImmutableSet<Context> convertContexts(@NonNull ContextSet contexts) {
return LP_TO_SPONGE_CACHE.get(contexts.makeImmutable());
}

View File

@ -0,0 +1,91 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.model;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.service.permission.PermissionDescription;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.Subject;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Predicate;
/**
* LuckPerms model for the Sponge {@link org.spongepowered.api.service.permission.PermissionService}
*/
public interface LPPermissionService {
LPSpongePlugin getPlugin();
PermissionService sponge();
LPSubjectCollection getUserSubjects();
LPSubjectCollection getGroupSubjects();
default LPSubjectData getDefaultData() {
return getDefaults().getSubjectData();
}
LPSubject getDefaults();
Predicate<String> getIdentifierValidityPredicate();
LPSubjectCollection getCollection(String identifier);
ImmutableMap<String, LPSubjectCollection> getLoadedCollections();
SubjectReference newSubjectReference(String collectionIdentifier, String subjectIdentifier);
PermissionDescription.Builder newDescriptionBuilder(Object plugin);
Optional<PermissionDescription> getDescription(String permission);
ImmutableCollection<PermissionDescription> getDescriptions();
void registerContextCalculator(ContextCalculator<Subject> calculator);
// utils
ImmutableList<SubjectReference> sortSubjects(Collection<SubjectReference> s);
Contexts calculateContexts(ImmutableContextSet contextSet);
void invalidatePermissionCaches();
void invalidateParentCaches();
void invalidateOptionCaches();
}

View File

@ -0,0 +1,94 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.model;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.SubjectProxy;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.permission.Subject;
import java.util.Optional;
/**
* LuckPerms model for the Sponge {@link org.spongepowered.api.service.permission.Subject}
*/
public interface LPSubject {
default Subject sponge() {
return new SubjectProxy(getService(), toReference());
}
LuckPermsService getService();
String getIdentifier();
default SubjectReference toReference() {
return getService().newSubjectReference(getParentCollection().getIdentifier(), getIdentifier());
}
default LPSubjectData getDefaultData() {
return getDefaults().getSubjectData();
}
default LPSubject getDefaults() {
return getService().getDefaultSubjects().loadSubject(getIdentifier()).join();
}
default Optional<String> getFriendlyIdentifier() {
return Optional.empty();
}
default Optional<CommandSource> getCommandSource() {
return Optional.empty();
}
LPSubjectCollection getParentCollection();
LPSubjectData getSubjectData();
LPSubjectData getTransientSubjectData();
Tristate getPermissionValue(ImmutableContextSet contexts, String permission);
boolean isChildOf(ImmutableContextSet contexts, SubjectReference parent);
ImmutableList<SubjectReference> getParents(ImmutableContextSet contexts);
Optional<String> getOption(ImmutableContextSet contexts, String key);
ImmutableContextSet getActiveContextSet();
default void performCleanup() {
}
}

View File

@ -0,0 +1,84 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.model;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import org.spongepowered.api.service.permission.SubjectCollection;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
/**
* LuckPerms model for the Sponge {@link org.spongepowered.api.service.permission.SubjectCollection}
*/
public interface LPSubjectCollection {
SubjectCollection sponge();
LuckPermsService getService();
String getIdentifier();
default SubjectReference newSubjectReference(String identifier) {
return getService().newSubjectReference(getIdentifier(), identifier);
}
Predicate<String> getIdentifierValidityPredicate();
CompletableFuture<LPSubject> loadSubject(String identifier);
Optional<LPSubject> getSubject(String identifier);
CompletableFuture<Boolean> hasRegistered(String identifier);
CompletableFuture<ImmutableCollection<LPSubject>> loadSubjects(Set<String> identifiers);
ImmutableCollection<LPSubject> getLoadedSubjects();
CompletableFuture<ImmutableSet<String>> getAllIdentifiers();
CompletableFuture<ImmutableMap<SubjectReference, Boolean>> getAllWithPermission(String permission);
CompletableFuture<ImmutableMap<SubjectReference, Boolean>> getAllWithPermission(ImmutableContextSet contexts, String permission);
ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission);
ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission);
LPSubject getDefaults();
void suggestUnload(String identifier);
}

View File

@ -0,0 +1,90 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import java.util.concurrent.CompletableFuture;
/**
* LuckPerms model for the Sponge {@link org.spongepowered.api.service.permission.SubjectData}
*/
public interface LPSubjectData {
LPSubject getParentSubject();
/* permissions */
ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions();
default ImmutableMap<String, Boolean> getPermissions(ImmutableContextSet contexts) {
return ImmutableMap.copyOf(getAllPermissions().getOrDefault(contexts, ImmutableMap.of()));
}
CompletableFuture<Boolean> setPermission(ImmutableContextSet contexts, String permission, Tristate value);
CompletableFuture<Boolean> clearPermissions();
CompletableFuture<Boolean> clearPermissions(ImmutableContextSet contexts);
/* parents */
ImmutableMap<ImmutableContextSet, ImmutableList<SubjectReference>> getAllParents();
default ImmutableList<SubjectReference> getParents(ImmutableContextSet contexts) {
return ImmutableList.copyOf(getAllParents().getOrDefault(contexts, ImmutableList.of()));
}
CompletableFuture<Boolean> addParent(ImmutableContextSet contexts, SubjectReference parent);
CompletableFuture<Boolean> removeParent(ImmutableContextSet contexts, SubjectReference parent);
CompletableFuture<Boolean> clearParents();
CompletableFuture<Boolean> clearParents(ImmutableContextSet contexts);
/* options */
ImmutableMap<ImmutableContextSet, ImmutableMap<String, String>> getAllOptions();
default ImmutableMap<String, String> getOptions(ImmutableContextSet contexts) {
return ImmutableMap.copyOf(getAllOptions().getOrDefault(contexts, ImmutableMap.of()));
}
CompletableFuture<Boolean> setOption(ImmutableContextSet contexts, String key, String value);
CompletableFuture<Boolean> unsetOption(ImmutableContextSet contexts, String key);
CompletableFuture<Boolean> clearOptions();
CompletableFuture<Boolean> clearOptions(ImmutableContextSet contexts);
}

View File

@ -27,67 +27,123 @@ package me.lucko.luckperms.sponge.service.persisted;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import me.lucko.luckperms.sponge.service.proxy.SubjectCollectionProxy;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.service.storage.SubjectStorageModel;
import java.util.Collection;
import org.spongepowered.api.service.permission.SubjectCollection;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
/**
* A simple persistable subject collection
*/
@Getter
@RequiredArgsConstructor
public class PersistedCollection implements LPSubjectCollection {
private final LuckPermsService service;
private final String identifier;
private final boolean transientHasPriority;
@Getter(AccessLevel.NONE)
private final SubjectCollectionProxy spongeProxy;
@Getter(AccessLevel.NONE)
private final LoadingCache<String, PersistedSubject> subjects = Caffeine.newBuilder()
.build(s -> new PersistedSubject(s, getService(), PersistedCollection.this));
public PersistedCollection(LuckPermsService service, String identifier) {
this.service = service;
this.identifier = identifier;
this.spongeProxy = new SubjectCollectionProxy(service, this);
}
public void loadAll() {
Map<String, SubjectStorageModel> holders = service.getStorage().loadAllFromFile(identifier);
for (Map.Entry<String, SubjectStorageModel> e : holders.entrySet()) {
PersistedSubject subject = get(e.getKey());
PersistedSubject subject = subjects.get(e.getKey().toLowerCase());
subject.loadData(e.getValue());
}
}
@Override
public PersistedSubject get(@NonNull String id) {
return subjects.get(id.toLowerCase());
public SubjectCollection sponge() {
return spongeProxy;
}
@Override
public boolean hasRegistered(@NonNull String id) {
return subjects.asMap().containsKey(id.toLowerCase());
public Predicate<String> getIdentifierValidityPredicate() {
return Predicates.alwaysTrue();
}
@Override
public Collection<LPSubject> getSubjects() {
return subjects.asMap().values().stream().map(s -> (LPSubject) s).collect(ImmutableCollectors.toImmutableList());
public CompletableFuture<LPSubject> loadSubject(String identifier) {
return CompletableFuture.completedFuture(subjects.get(identifier.toLowerCase()));
}
@Override
public Map<LPSubject, Boolean> getWithPermission(@NonNull ContextSet contexts, @NonNull String node) {
public Optional<LPSubject> getSubject(String identifier) {
return Optional.of(subjects.get(identifier.toLowerCase()));
}
@Override
public CompletableFuture<Boolean> hasRegistered(String identifier) {
return CompletableFuture.completedFuture(subjects.asMap().containsKey(identifier.toLowerCase()));
}
@Override
public CompletableFuture<ImmutableCollection<LPSubject>> loadSubjects(Set<String> identifiers) {
ImmutableSet.Builder<LPSubject> ret = ImmutableSet.builder();
for (String id : identifiers) {
ret.add(subjects.get(id.toLowerCase()));
}
return CompletableFuture.completedFuture(ret.build());
}
@Override
public ImmutableCollection<LPSubject> getLoadedSubjects() {
return ImmutableList.copyOf(subjects.asMap().values());
}
@Override
public CompletableFuture<ImmutableSet<String>> getAllIdentifiers() {
return CompletableFuture.completedFuture(ImmutableSet.copyOf(subjects.asMap().keySet()));
}
@Override
public CompletableFuture<ImmutableMap<SubjectReference, Boolean>> getAllWithPermission(String permission) {
return CompletableFuture.completedFuture(getLoadedWithPermission(permission).entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(e -> e.getKey().toReference(), Map.Entry::getValue)));
}
@Override
public CompletableFuture<ImmutableMap<SubjectReference, Boolean>> getAllWithPermission(ImmutableContextSet contexts, String permission) {
return CompletableFuture.completedFuture(getLoadedWithPermission(contexts, permission).entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(e -> e.getKey().toReference(), Map.Entry::getValue)));
}
@Override
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(String permission) {
ImmutableMap.Builder<LPSubject, Boolean> m = ImmutableMap.builder();
for (LPSubject subject : subjects.asMap().values()) {
Tristate ts = subject.getPermissionValue(contexts, node);
Tristate ts = subject.getPermissionValue(ImmutableContextSet.empty(), permission);
if (ts != Tristate.UNDEFINED) {
m.put(subject, ts.asBoolean());
}
@ -97,12 +153,25 @@ public class PersistedCollection implements LPSubjectCollection {
}
@Override
public SubjectReference getDefaultSubject() {
return SubjectReference.of("defaults", identifier);
public ImmutableMap<LPSubject, Boolean> getLoadedWithPermission(ImmutableContextSet contexts, String permission) {
ImmutableMap.Builder<LPSubject, Boolean> m = ImmutableMap.builder();
for (LPSubject subject : subjects.asMap().values()) {
Tristate ts = subject.getPermissionValue(contexts, permission);
if (ts != Tristate.UNDEFINED) {
m.put(subject, ts.asBoolean());
}
}
return m.build();
}
@Override
public boolean getTransientHasPriority() {
return transientHasPriority;
public LPSubject getDefaults() {
return service.getDefaultSubjects().loadSubject(getIdentifier()).join();
}
@Override
public void suggestUnload(String identifier) {
// ignore
}
}

View File

@ -30,18 +30,16 @@ import lombok.NonNull;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.sponge.service.LuckPermsService;
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.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import me.lucko.luckperms.sponge.service.storage.SubjectStorageModel;
import me.lucko.luckperms.sponge.timings.LPTiming;
@ -51,9 +49,9 @@ import org.spongepowered.api.command.CommandSource;
import co.aikar.timings.Timing;
import java.io.IOException;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@ -63,9 +61,9 @@ import java.util.concurrent.TimeUnit;
public class PersistedSubject implements LPSubject {
private final String identifier;
@Getter
private final LuckPermsService service;
private final SubjectCollectionReference parentCollection;
private final PersistedCollection parentCollection;
private final PersistedSubjectData subjectData;
private final CalculatedSubjectData transientSubjectData;
@ -73,7 +71,7 @@ public class PersistedSubject implements LPSubject {
.expireAfterAccess(20, TimeUnit.MINUTES)
.build(lookup -> lookupPermissionValue(lookup.getContexts(), lookup.getNode()));
private final LoadingCache<ImmutableContextSet, Set<SubjectReference>> parentLookupCache = Caffeine.newBuilder()
private final LoadingCache<ImmutableContextSet, ImmutableList<SubjectReference>> parentLookupCache = Caffeine.newBuilder()
.expireAfterAccess(20, TimeUnit.MINUTES)
.build(this::lookupParents);
@ -95,13 +93,13 @@ public class PersistedSubject implements LPSubject {
}
};
public PersistedSubject(String identifier, LuckPermsService service, PersistedCollection containingCollection) {
public PersistedSubject(String identifier, LuckPermsService service, PersistedCollection parentCollection) {
this.identifier = identifier;
this.service = service;
this.parentCollection = containingCollection.toReference();
this.parentCollection = parentCollection;
this.subjectData = new PersistedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(p)", this);
this.transientSubjectData = new CalculatedSubjectData(this, service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(t)");
this.subjectData = new PersistedSubjectData(service, "local:" + parentCollection.getIdentifier() + "/" + identifier + "(p)", this);
this.transientSubjectData = new CalculatedSubjectData(this, service, "local:" + parentCollection.getIdentifier() + "/" + identifier + "(t)");
service.getLocalDataCaches().add(subjectData);
service.getLocalDataCaches().add(transientSubjectData);
@ -134,10 +132,11 @@ public class PersistedSubject implements LPSubject {
return Optional.empty();
}
private Tristate lookupPermissionValue(ContextSet contexts, String node) {
private Tristate lookupPermissionValue(ImmutableContextSet contexts, String node) {
Tristate res;
if (parentCollection.resolve(service).getTransientHasPriority()) {
// if transient has priority
if (!parentCollection.getIdentifier().equals("defaults")) {
res = transientSubjectData.getPermissionValue(contexts, node);
if (res != Tristate.UNDEFINED) {
return res;
@ -160,17 +159,17 @@ public class PersistedSubject implements LPSubject {
}
for (SubjectReference parent : getParents(contexts)) {
res = parent.resolve(service).getPermissionValue(contexts, node);
res = parent.resolve().join().getPermissionValue(contexts, node);
if (res != Tristate.UNDEFINED) {
return res;
}
}
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
if (getParentCollection().getIdentifier().equalsIgnoreCase("defaults")) {
return Tristate.UNDEFINED;
}
res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getPermissionValue(contexts, node);
res = getParentCollection().getDefaults().getPermissionValue(contexts, node);
if (res != Tristate.UNDEFINED) {
return res;
}
@ -179,23 +178,24 @@ public class PersistedSubject implements LPSubject {
return res;
}
private Set<SubjectReference> lookupParents(ContextSet contexts) {
Set<SubjectReference> s = new HashSet<>();
private ImmutableList<SubjectReference> lookupParents(ImmutableContextSet contexts) {
List<SubjectReference> s = new ArrayList<>();
s.addAll(subjectData.getParents(contexts));
s.addAll(transientSubjectData.getParents(contexts));
if (!getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
s.addAll(getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts));
if (!getParentCollection().getIdentifier().equalsIgnoreCase("defaults")) {
s.addAll(getParentCollection().getDefaults().getParents(contexts));
s.addAll(service.getDefaults().getParents(contexts));
}
return ImmutableSet.copyOf(s);
return service.sortSubjects(s);
}
private Optional<String> lookupOptionValue(ContextSet contexts, String key) {
private Optional<String> lookupOptionValue(ImmutableContextSet contexts, String key) {
Optional<String> res;
if (getParentCollection().resolve(service).getTransientHasPriority()) {
// if transient has priority
if (!parentCollection.getIdentifier().equals("defaults")) {
res = Optional.ofNullable(transientSubjectData.getOptions(contexts).get(key));
if (res.isPresent()) {
return res;
@ -218,17 +218,17 @@ public class PersistedSubject implements LPSubject {
}
for (SubjectReference parent : getParents(contexts)) {
res = parent.resolve(service).getOption(contexts, key);
res = parent.resolve().join().getOption(contexts, key);
if (res.isPresent()) {
return res;
}
}
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
if (getParentCollection().getIdentifier().equalsIgnoreCase("defaults")) {
return Optional.empty();
}
res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getOption(contexts, key);
res = getParentCollection().getDefaults().getOption(contexts, key);
if (res.isPresent()) {
return res;
}
@ -237,47 +237,47 @@ public class PersistedSubject implements LPSubject {
}
@Override
public Tristate getPermissionValue(@NonNull ContextSet contexts, @NonNull String node) {
public Tristate getPermissionValue(@NonNull ImmutableContextSet contexts, @NonNull String node) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PERMISSION_VALUE)) {
Tristate t = permissionLookupCache.get(PermissionLookup.of(node, contexts.makeImmutable()));
service.getPlugin().getVerboseHandler().offer("local:" + getParentCollection().getCollection() + "/" + identifier, node, t);
Tristate t = permissionLookupCache.get(PermissionLookup.of(node, contexts));
service.getPlugin().getVerboseHandler().offer("local:" + getParentCollection().getIdentifier() + "/" + identifier, node, t);
return t;
}
}
@Override
public boolean isChildOf(@NonNull ContextSet contexts, @NonNull SubjectReference subject) {
public boolean isChildOf(@NonNull ImmutableContextSet contexts, @NonNull SubjectReference subject) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_IS_CHILD_OF)) {
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
if (getParentCollection().getIdentifier().equalsIgnoreCase("defaults")) {
return subjectData.getParents(contexts).contains(subject) ||
transientSubjectData.getParents(contexts).contains(subject);
} else {
return subjectData.getParents(contexts).contains(subject) ||
transientSubjectData.getParents(contexts).contains(subject) ||
getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts).contains(subject) ||
getParentCollection().getDefaults().getParents(contexts).contains(subject) ||
service.getDefaults().getParents(contexts).contains(subject);
}
}
}
@Override
public Set<SubjectReference> getParents(@NonNull ContextSet contexts) {
public ImmutableList<SubjectReference> getParents(@NonNull ImmutableContextSet contexts) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PARENTS)) {
return parentLookupCache.get(contexts.makeImmutable());
return parentLookupCache.get(contexts);
}
}
@Override
public Optional<String> getOption(ContextSet contexts, String key) {
public Optional<String> getOption(ImmutableContextSet contexts, String key) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_OPTION)) {
return optionLookupCache.get(OptionLookup.of(key, contexts.makeImmutable()));
return optionLookupCache.get(OptionLookup.of(key, contexts));
}
}
@Override
public ContextSet getActiveContextSet() {
public ImmutableContextSet getActiveContextSet() {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_ACTIVE_CONTEXTS)) {
return service.getPlugin().getContextManager().getApplicableContext(this);
return service.getPlugin().getContextManager().getApplicableContext(sponge()).makeImmutable();
}
}
}

View File

@ -28,15 +28,19 @@ package me.lucko.luckperms.sponge.service.persisted;
import lombok.Getter;
import lombok.Setter;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
/**
* Extension of MemorySubjectData which persists data when modified
*/
public class PersistedSubjectData extends CalculatedSubjectData {
public class PersistedSubjectData extends CalculatedSubjectData implements Function<Boolean, Boolean> {
private final PersistedSubject subject;
@Getter
@ -59,79 +63,63 @@ public class PersistedSubjectData extends CalculatedSubjectData {
}
@Override
public boolean setPermission(ContextSet contexts, String permission, me.lucko.luckperms.api.Tristate value) {
boolean r = super.setPermission(contexts, permission, value);
public Boolean apply(Boolean b) {
save();
return r;
return b;
}
@Override
public boolean clearPermissions() {
boolean r = super.clearPermissions();
save();
return r;
public CompletableFuture<Boolean> setPermission(ImmutableContextSet contexts, String permission, Tristate value) {
return super.setPermission(contexts, permission, value).thenApply(this);
}
@Override
public boolean clearPermissions(ContextSet contexts) {
boolean r = super.clearPermissions(contexts);
save();
return r;
public CompletableFuture<Boolean> clearPermissions() {
return super.clearPermissions().thenApply(this);
}
@Override
public boolean addParent(ContextSet contexts, SubjectReference parent) {
boolean r = super.addParent(contexts, parent);
save();
return r;
public CompletableFuture<Boolean> clearPermissions(ImmutableContextSet contexts) {
return super.clearPermissions(contexts).thenApply(this);
}
@Override
public boolean removeParent(ContextSet contexts, SubjectReference parent) {
boolean r = super.removeParent(contexts, parent);
save();
return r;
public CompletableFuture<Boolean> addParent(ImmutableContextSet contexts, SubjectReference parent) {
return super.addParent(contexts, parent).thenApply(this);
}
@Override
public boolean clearParents() {
boolean r = super.clearParents();
save();
return r;
public CompletableFuture<Boolean> removeParent(ImmutableContextSet contexts, SubjectReference parent) {
return super.removeParent(contexts, parent).thenApply(this);
}
@Override
public boolean clearParents(ContextSet contexts) {
boolean r = super.clearParents(contexts);
save();
return r;
public CompletableFuture<Boolean> clearParents() {
return super.clearParents().thenApply(this);
}
@Override
public boolean setOption(ContextSet contexts, String key, String value) {
boolean r = super.setOption(contexts, key, value);
save();
return r;
public CompletableFuture<Boolean> clearParents(ImmutableContextSet contexts) {
return super.clearParents(contexts).thenApply(this);
}
@Override
public boolean unsetOption(ContextSet contexts, String key) {
boolean r = super.unsetOption(contexts, key);
save();
return r;
public CompletableFuture<Boolean> setOption(ImmutableContextSet contexts, String key, String value) {
return super.setOption(contexts, key, value).thenApply(this);
}
@Override
public boolean clearOptions(ContextSet contexts) {
boolean r = super.clearOptions(contexts);
save();
return r;
public CompletableFuture<Boolean> unsetOption(ImmutableContextSet contexts, String key) {
return super.unsetOption(contexts, key).thenApply(this);
}
@Override
public boolean clearOptions() {
boolean r = super.clearOptions();
save();
return r;
public CompletableFuture<Boolean> clearOptions() {
return super.clearOptions().thenApply(this);
}
@Override
public CompletableFuture<Boolean> clearOptions(ImmutableContextSet contexts) {
return super.clearOptions(contexts).thenApply(this);
}
}

View File

@ -1,159 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.proxy;
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.proxy.Util.convertContexts;
import static me.lucko.luckperms.sponge.service.proxy.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 void performCleanup() {
}
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 */
@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() {
List<Subject> ret = getParents(getActiveContextSet()).stream().map(s -> s.resolve(getService())).collect(ImmutableCollectors.toImmutableList());
return getService().sortSubjects(ret);
}
@Deprecated
@Override
default List<Subject> getParents(@NonNull Set<Context> contexts) {
List<Subject> ret = getParents(convertContexts(contexts)).stream().map(s -> s.resolve(getService())).collect(ImmutableCollectors.toImmutableList());
return getService().sortSubjects(ret);
}
@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(convertContexts(contexts), key);
}
@Deprecated
@Override
default Set<Context> getActiveContexts() {
return convertContexts(getActiveContextSet());
}
}

View File

@ -1,99 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.proxy;
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.proxy.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();
boolean getTransientHasPriority();
@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

@ -1,194 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.proxy;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
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 java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import static me.lucko.luckperms.sponge.service.proxy.Util.convertContexts;
import static me.lucko.luckperms.sponge.service.proxy.Util.convertTristate;
public interface LPSubjectData extends SubjectData {
LPSubject getParentSubject();
Map<ImmutableContextSet, 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<ImmutableContextSet, 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<ImmutableContextSet, 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,96 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.proxy;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.contexts.SpongeCalculatorLink;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import org.spongepowered.api.service.context.ContextCalculator;
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 java.util.Collection;
import java.util.Map;
import java.util.Optional;
@RequiredArgsConstructor
public class PermissionServiceProxy implements PermissionService {
private final LPPermissionService handle;
@Override
public SubjectCollection getUserSubjects() {
return handle.getUserSubjects().sponge();
}
@Override
public SubjectCollection getGroupSubjects() {
return handle.getGroupSubjects().sponge();
}
@Override
public Subject getDefaults() {
return handle.getDefaults().sponge();
}
@Override
public SubjectCollection getSubjects(String s) {
return handle.getCollection(s).sponge();
}
@Override
public Map<String, SubjectCollection> getKnownSubjects() {
return handle.getLoadedCollections().entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
Map.Entry::getKey,
e -> e.getValue().sponge()
));
}
@Override
public Optional<PermissionDescription.Builder> newDescriptionBuilder(Object o) {
return Optional.of(handle.newDescriptionBuilder(o));
}
@Override
public Optional<PermissionDescription> getDescription(String s) {
return handle.getDescription(s);
}
@Override
public Collection<PermissionDescription> getDescriptions() {
return handle.getDescriptions();
}
@Override
public void registerContextCalculator(ContextCalculator<Subject> contextCalculator) {
handle.registerContextCalculator(new SpongeCalculatorLink(contextCalculator));
}
}

View File

@ -0,0 +1,106 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.proxy;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.model.CompatibilityUtil;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectCollection;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@SuppressWarnings("unchecked")
@RequiredArgsConstructor
public class SubjectCollectionProxy implements SubjectCollection {
private final LPPermissionService service;
private final LPSubjectCollection handle;
@Override
public String getIdentifier() {
return handle.getIdentifier();
}
@Override
public Subject get(String s) {
// force load the subject.
// after this call, users will expect that the subject is loaded in memory.
return handle.loadSubject(s).thenApply(LPSubject::sponge).join();
}
@Override
public boolean hasRegistered(String s) {
return handle.hasRegistered(s).join();
}
@Override
public Iterable<Subject> getAllSubjects() {
// this will lazily load all subjects. it will initially just get the identifiers of each subject, and will initialize dummy
// providers for those identifiers. when any methods against the dummy are called, the actual data will be loaded.
// this behaviour should be replaced when CompletableFutures are added to Sponge
return (List) handle.getAllIdentifiers()
.thenApply(ids -> ids.stream()
.map(s -> new SubjectProxy(service, service.newSubjectReference(getIdentifier(), s)))
.collect(ImmutableCollectors.toImmutableList())
).join();
}
@Override
public Map<Subject, Boolean> getAllWithPermission(String s) {
// again, these methods will lazily load subjects.
return (Map) handle.getAllWithPermission(s).thenApply(map -> {
return map.entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
e -> new SubjectProxy(service, e.getKey()),
Map.Entry::getValue
));
}).join();
}
@Override
public Map<Subject, Boolean> getAllWithPermission(Set<Context> set, String s) {
return (Map) handle.getAllWithPermission(CompatibilityUtil.convertContexts(set), s)
.thenApply(map -> map.entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
e -> new SubjectProxy(service, e.getKey()),
Map.Entry::getValue
))
).join();
}
@Override
public Subject getDefaults() {
return handle.getDefaults().sponge();
}
}

View File

@ -0,0 +1,205 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.proxy;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.model.CompatibilityUtil;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPSubjectData;
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 java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
/**
* Proxies a LuckPerms Subject to implement {@link SubjectData}.
*
* All methods which return "boolean" will return instantly, and the change will be applied in the background.
* This will be changed as soon as Sponge implements futures into its API.
*/
@SuppressWarnings("unchecked")
@RequiredArgsConstructor
public class SubjectDataProxy implements SubjectData {
private final LPPermissionService service;
private final SubjectReference ref;
private final boolean enduring;
private CompletableFuture<LPSubjectData> getHandle() {
return enduring ? ref.resolve().thenApply(LPSubject::getSubjectData) : ref.resolve().thenApply(LPSubject::getTransientSubjectData);
}
@Override
public Map<Set<Context>, Map<String, Boolean>> getAllPermissions() {
return (Map) getHandle().thenApply(handle -> {
return handle.getAllPermissions().entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
e -> CompatibilityUtil.convertContexts(e.getKey()),
Map.Entry::getValue
));
}).join();
}
@Override
public Map<String, Boolean> getPermissions(Set<Context> contexts) {
return getHandle().thenApply(handle -> handle.getPermissions(CompatibilityUtil.convertContexts(contexts))).join();
}
@Override
public boolean setPermission(Set<Context> contexts, String permission, Tristate value) {
getHandle().thenCompose(handle -> {
return handle.setPermission(
CompatibilityUtil.convertContexts(contexts),
permission,
CompatibilityUtil.convertTristate(value)
);
});
return true;
}
@Override
public boolean clearPermissions() {
getHandle().thenCompose(LPSubjectData::clearPermissions);
return true;
}
@Override
public boolean clearPermissions(Set<Context> contexts) {
getHandle().thenCompose(handle -> handle.clearPermissions(CompatibilityUtil.convertContexts(contexts)));
return true;
}
@Override
public Map<Set<Context>, List<Subject>> getAllParents() {
return (Map) getHandle().thenApply(handle -> {
return handle.getAllParents().entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
e -> CompatibilityUtil.convertContexts(e.getKey()),
e -> e.getValue().stream()
.map(s -> new SubjectProxy(service, s))
.collect(ImmutableCollectors.toImmutableList())
)
);
}).join();
}
@Override
public List<Subject> getParents(Set<Context> contexts) {
return (List) getHandle().thenApply(handle -> {
return handle.getParents(CompatibilityUtil.convertContexts(contexts)).stream()
.map(s -> new SubjectProxy(service, s))
.collect(ImmutableCollectors.toImmutableList());
}).join();
}
@Override
public boolean addParent(Set<Context> contexts, Subject parent) {
getHandle().thenCompose(handle -> {
return handle.addParent(
CompatibilityUtil.convertContexts(contexts),
service.newSubjectReference(
parent.getContainingCollection().getIdentifier(),
parent.getIdentifier()
)
);
});
return true;
}
@Override
public boolean removeParent(Set<Context> contexts, Subject parent) {
getHandle().thenCompose(handle -> {
return handle.removeParent(
CompatibilityUtil.convertContexts(contexts),
service.newSubjectReference(
parent.getContainingCollection().getIdentifier(),
parent.getIdentifier()
)
);
});
return true;
}
@Override
public boolean clearParents() {
getHandle().thenCompose(LPSubjectData::clearParents);
return true;
}
@Override
public boolean clearParents(Set<Context> contexts) {
getHandle().thenCompose(handle -> handle.clearParents(CompatibilityUtil.convertContexts(contexts)));
return true;
}
@Override
public Map<Set<Context>, Map<String, String>> getAllOptions() {
return (Map) getHandle().thenApply(handle -> {
return handle.getAllOptions().entrySet().stream()
.collect(ImmutableCollectors.toImmutableMap(
e -> CompatibilityUtil.convertContexts(e.getKey()),
Map.Entry::getValue
));
}).join();
}
@Override
public Map<String, String> getOptions(Set<Context> contexts) {
return getHandle().thenApply(handle -> handle.getOptions(CompatibilityUtil.convertContexts(contexts))).join();
}
@Override
public boolean setOption(Set<Context> contexts, String key, String value) {
if (value == null) {
getHandle().thenCompose(handle -> handle.unsetOption(CompatibilityUtil.convertContexts(contexts), key));
return true;
} else {
getHandle().thenCompose(handle -> handle.setOption(CompatibilityUtil.convertContexts(contexts), key, value));
return true;
}
}
@Override
public boolean clearOptions(Set<Context> contexts) {
getHandle().thenCompose(handle -> handle.clearOptions(CompatibilityUtil.convertContexts(contexts)));
return true;
}
@Override
public boolean clearOptions() {
getHandle().thenCompose(LPSubjectData::clearOptions);
return true;
}
}

View File

@ -0,0 +1,167 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.proxy;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.sponge.service.model.CompatibilityUtil;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubject;
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.service.permission.SubjectCollection;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@SuppressWarnings("unchecked")
@RequiredArgsConstructor
public class SubjectProxy implements Subject {
private final LPPermissionService service;
private final SubjectReference ref;
private CompletableFuture<LPSubject> getHandle() {
return ref.resolve();
}
@Override
public Optional<CommandSource> getCommandSource() {
return getHandle().thenApply(LPSubject::getCommandSource).join();
}
@Override
public SubjectCollection getContainingCollection() {
return service.getCollection(ref.getCollection()).sponge();
}
@Override
public SubjectData getSubjectData() {
return new SubjectDataProxy(service, ref, true);
}
@Override
public SubjectData getTransientSubjectData() {
return new SubjectDataProxy(service, ref, false);
}
@Override
public boolean hasPermission(Set<Context> contexts, String permission) {
return getHandle().thenApply(handle -> {
return handle.getPermissionValue(CompatibilityUtil.convertContexts(contexts), permission).asBoolean();
}).join();
}
@Override
public boolean hasPermission(String permission) {
return getHandle().thenApply(handle -> {
return handle.getPermissionValue(ImmutableContextSet.empty(), permission).asBoolean();
}).join();
}
@Override
public Tristate getPermissionValue(Set<Context> contexts, String permission) {
return getHandle().thenApply(handle -> {
return CompatibilityUtil.convertTristate(handle.getPermissionValue(CompatibilityUtil.convertContexts(contexts), permission));
}).join();
}
@Override
public boolean isChildOf(Subject parent) {
return getHandle().thenApply(handle -> {
return handle.isChildOf(
ImmutableContextSet.empty(),
service.newSubjectReference(
parent.getContainingCollection().getIdentifier(),
parent.getIdentifier()
)
);
}).join();
}
@Override
public boolean isChildOf(Set<Context> contexts, Subject parent) {
return getHandle().thenApply(handle -> {
return handle.isChildOf(
CompatibilityUtil.convertContexts(contexts),
service.newSubjectReference(
parent.getContainingCollection().getIdentifier(),
parent.getIdentifier()
)
);
}).join();
}
@Override
public List<Subject> getParents() {
return (List) getHandle().thenApply(handle -> {
return handle.getParents(ImmutableContextSet.empty()).stream()
.map(s -> new SubjectProxy(service, s))
.collect(ImmutableCollectors.toImmutableList());
}).join();
}
@Override
public List<Subject> getParents(Set<Context> contexts) {
return (List) getHandle().thenApply(handle -> {
return handle.getParents(CompatibilityUtil.convertContexts(contexts)).stream()
.map(s -> new SubjectProxy(service, s))
.collect(ImmutableCollectors.toImmutableList());
}).join();
}
@Override
public Optional<String> getOption(Set<Context> contexts, String key) {
return getHandle().thenApply(handle -> {
return handle.getOption(CompatibilityUtil.convertContexts(contexts), key);
}).join();
}
@Override
public Optional<String> getOption(String key) {
return getHandle().thenApply(handle -> {
return handle.getOption(ImmutableContextSet.empty(), key);
}).join();
}
@Override
public String getIdentifier() {
return ref.getIdentifier();
}
@Override
public Set<Context> getActiveContexts() {
return getHandle().thenApply(handle -> CompatibilityUtil.convertContexts(handle.getActiveContextSet())).join();
}
}

View File

@ -1,60 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.LPSubjectCollection;
import java.lang.ref.WeakReference;
@ToString(of = "collection")
@EqualsAndHashCode(of = "collection")
@RequiredArgsConstructor(staticName = "of")
public final class SubjectCollectionReference {
@Getter
private final String collection;
private WeakReference<LPSubjectCollection> ref = null;
public synchronized LPSubjectCollection resolve(LuckPermsService service) {
if (ref != null) {
LPSubjectCollection sc = ref.get();
if (sc != null) {
return sc;
}
}
LPSubjectCollection sc = service.getSubjects(collection);
ref = new WeakReference<>(sc);
return sc;
}
}

View File

@ -32,46 +32,75 @@ import lombok.ToString;
import com.google.common.base.Splitter;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.proxy.LPSubject;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubject;
import org.spongepowered.api.service.permission.Subject;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@ToString(of = {"collection", "identifier"})
@EqualsAndHashCode(of = {"collection", "identifier"})
@RequiredArgsConstructor(staticName = "of")
public final class SubjectReference {
public static SubjectReference deserialize(String s) {
@Deprecated
public static SubjectReference deserialize(LPPermissionService service, String s) {
List<String> parts = Splitter.on('/').limit(2).splitToList(s);
return of(parts.get(0), parts.get(1));
return of(service, parts.get(0), parts.get(1));
}
public static SubjectReference of(Subject subject) {
return of(subject.getContainingCollection().getIdentifier(), subject.getIdentifier());
public static SubjectReference of(LPPermissionService service, Subject subject) {
return of(service, subject.getContainingCollection().getIdentifier(), subject.getIdentifier());
}
private final LPPermissionService service;
@Getter
private final String collection;
@Getter
private final String identifier;
private WeakReference<LPSubject> ref = null;
private long lastLookup = 0L;
private WeakReference<LPSubject> cache = null;
public synchronized LPSubject resolve(LuckPermsService service) {
if (ref != null) {
LPSubject s = ref.get();
if (s != null) {
return s;
private synchronized LPSubject resolveDirectly() {
long sinceLast = System.currentTimeMillis() - lastLookup;
// try the cache
if (sinceLast < TimeUnit.SECONDS.toMillis(10)) {
if (cache != null) {
LPSubject s = cache.get();
if (s != null) {
return s;
}
}
}
LPSubject s = service.getSubjects(collection).get(identifier);
ref = new WeakReference<>(s);
LPSubject s = service.getCollection(collection).loadSubject(identifier).join();
lastLookup = System.currentTimeMillis();
cache = new WeakReference<>(s);
return s;
}
public CompletableFuture<LPSubject> resolve() {
long sinceLast = System.currentTimeMillis() - lastLookup;
// try the cache
if (sinceLast < TimeUnit.SECONDS.toMillis(10)) {
if (cache != null) {
LPSubject s = cache.get();
if (s != null) {
return CompletableFuture.completedFuture(s);
}
}
}
return CompletableFuture.supplyAsync(this::resolveDirectly);
}
}

View File

@ -33,6 +33,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.persisted.PersistedSubject;
import java.io.BufferedReader;
@ -52,12 +53,15 @@ import java.util.stream.Collectors;
*/
public class SubjectStorage {
private final LPPermissionService service;
@Getter
private final Gson gson;
private final File container;
public SubjectStorage(File container) {
public SubjectStorage(LPPermissionService service, File container) {
this.service = service;
this.gson = new GsonBuilder().setPrettyPrinting().create();
this.container = container;
checkContainer();
@ -89,7 +93,7 @@ public class SubjectStorage {
}
public void saveToFile(PersistedSubject subject) throws IOException {
File subjectFile = resolveFile(subject.getContainingCollection().getIdentifier(), subject.getIdentifier());
File subjectFile = resolveFile(subject.getParentCollection().getIdentifier(), subject.getIdentifier());
saveToFile(new SubjectStorageModel(subject.getSubjectData()), subjectFile);
}
@ -153,7 +157,7 @@ public class SubjectStorage {
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
JsonObject data = gson.fromJson(reader, JsonObject.class);
SubjectStorageModel model = new SubjectStorageModel(data);
SubjectStorageModel model = new SubjectStorageModel(service, data);
return Maps.immutableEntry(subjectName, model);
}
}

View File

@ -39,6 +39,7 @@ import me.lucko.luckperms.common.core.ContextSetComparator;
import me.lucko.luckperms.common.core.NodeModel;
import me.lucko.luckperms.common.core.PriorityComparator;
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.references.SubjectReference;
import java.util.ArrayList;
@ -50,35 +51,40 @@ import java.util.Map;
*/
@Getter
public class SubjectStorageModel {
private final LPPermissionService service;
private final Map<ImmutableContextSet, Map<String, Boolean>> permissions;
private final Map<ImmutableContextSet, Map<String, String>> options;
private final Map<ImmutableContextSet, List<SubjectReference>> parents;
public SubjectStorageModel(Map<ImmutableContextSet, Map<String, Boolean>> permissions, Map<ImmutableContextSet, Map<String, String>> options, Map<ImmutableContextSet, List<SubjectReference>> parents) {
public SubjectStorageModel(LPPermissionService service, Map<ImmutableContextSet, ? extends Map<String, Boolean>> permissions, Map<ImmutableContextSet, ? extends Map<String, String>> options, Map<ImmutableContextSet, ? extends List<SubjectReference>> parents) {
this.service = service;
ImmutableMap.Builder<ImmutableContextSet, Map<String, Boolean>> permissionsBuilder = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Map<String, Boolean>> e : permissions.entrySet()) {
for (Map.Entry<ImmutableContextSet, ? extends Map<String, Boolean>> e : permissions.entrySet()) {
permissionsBuilder.put(e.getKey(), ImmutableMap.copyOf(e.getValue()));
}
this.permissions = permissionsBuilder.build();
ImmutableMap.Builder<ImmutableContextSet, Map<String, String>> optionsBuilder = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Map<String, String>> e : options.entrySet()) {
for (Map.Entry<ImmutableContextSet, ? extends Map<String, String>> e : options.entrySet()) {
optionsBuilder.put(e.getKey(), ImmutableMap.copyOf(e.getValue()));
}
this.options = optionsBuilder.build();
ImmutableMap.Builder<ImmutableContextSet, List<SubjectReference>> parentsBuilder = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, List<SubjectReference>> e : parents.entrySet()) {
for (Map.Entry<ImmutableContextSet, ? extends List<SubjectReference>> e : parents.entrySet()) {
parentsBuilder.put(e.getKey(), ImmutableList.copyOf(e.getValue()));
}
this.parents = parentsBuilder.build();
}
public SubjectStorageModel(CalculatedSubjectData data) {
this(data.getPermissions(), data.getOptions(), data.getParentsAsList());
this(data.getParentSubject().getService(), data.getAllPermissions(), data.getAllOptions(), data.getAllParents());
}
public SubjectStorageModel(JsonObject root) {
public SubjectStorageModel(LPPermissionService service, JsonObject root) {
this.service = service;
Preconditions.checkArgument(root.get("permissions").isJsonArray());
Preconditions.checkArgument(root.get("options").isJsonArray());
Preconditions.checkArgument(root.get("parents").isJsonArray());
@ -158,7 +164,7 @@ public class SubjectStorageModel {
String collection = parent.get("collection").getAsString();
String subject = parent.get("subject").getAsString();
pars.add(SubjectReference.of(collection, subject));
pars.add(SubjectReference.of(service, collection, subject));
}
parentsBuilder.put(contextSet, pars.build());