From 8326539d406e3ed1affa2861522b42ad33f91ce1 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 3 May 2017 20:14:31 +0100 Subject: [PATCH] Refactor LP PermissionService to implement a modified API & add proxied classes --- .../luckperms/bukkit/BukkitListener.java | 2 +- .../luckperms/bukkit/LPBukkitPlugin.java | 4 +- .../migration/MigrationBPermissions.java | 2 +- .../migration/MigrationGroupManager.java | 2 +- .../migration/MigrationPermissionsEx.java | 2 +- .../migration/MigrationPowerfulPerms.java | 2 +- .../migration/MigrationZPermissions.java | 2 +- .../luckperms/bungee/BungeeListener.java | 6 +- .../migration/MigrationBungeePerms.java | 2 +- .../luckperms/common/api/ApiProvider.java | 2 +- .../commands/impl/misc/CheckCommand.java | 2 +- .../commands/impl/misc/TreeCommand.java | 2 +- .../commands/impl/user/UserMainCommand.java | 2 +- .../common/commands/utils/ArgumentUtils.java | 6 +- .../lucko/luckperms/common/data/Exporter.java | 2 +- .../luckperms/common/managers/Manager.java | 2 + .../common/managers/UserManager.java | 2 +- .../managers/impl/GenericUserManager.java | 4 +- .../common/storage/AbstractStorage.java | 2 +- .../storage/backing/FlatfileBacking.java | 2 +- .../luckperms/common/utils/LoginHelper.java | 4 +- .../luckperms/sponge/LPSpongePlugin.java | 33 ++- .../sponge/SpongeCalculatorFactory.java | 2 +- .../luckperms/sponge/SpongeListener.java | 6 +- .../sponge/calculators/DefaultsProcessor.java | 6 +- .../sponge/commands/OptionClear.java | 6 +- .../luckperms/sponge/commands/OptionInfo.java | 11 +- .../luckperms/sponge/commands/OptionSet.java | 8 +- .../sponge/commands/OptionUnset.java | 8 +- .../luckperms/sponge/commands/ParentAdd.java | 22 +- .../sponge/commands/ParentClear.java | 6 +- .../luckperms/sponge/commands/ParentInfo.java | 14 +- .../sponge/commands/ParentRemove.java | 22 +- .../sponge/commands/PermissionClear.java | 6 +- .../sponge/commands/PermissionInfo.java | 11 +- .../sponge/commands/PermissionSet.java | 8 +- .../sponge/commands/SpongeMainCommand.java | 18 +- .../sponge/commands/SpongeUtils.java | 3 +- .../sponge/contexts/SpongeCalculatorLink.java | 4 +- .../sponge/managers/SpongeGroupManager.java | 171 +++++++++---- .../sponge/managers/SpongeUserManager.java | 238 +++++++++++++----- .../migration/MigrationPermissionManager.java | 6 +- .../migration/MigrationPermissionsEx.java | 6 +- .../migration/SpongeMigrationUtils.java | 8 +- .../luckperms/sponge/model/SpongeGroup.java | 51 ++-- .../luckperms/sponge/model/SpongeUser.java | 54 ++-- .../sponge/service/LuckPermsService.java | 165 ++++-------- .../sponge/service/LuckPermsSubjectData.java | 172 ++++++------- .../service/ServiceCacheHousekeepingTask.java | 8 +- .../calculated/CalculatedSubjectData.java | 152 +++++------ .../description/SimpleDescription.java | 70 ++++++ .../description/SimpleDescriptionBuilder.java | 103 ++++++++ .../legacystorage/LegacyDataMigrator.java | 6 +- .../legacystorage/SubjectDataHolder.java | 15 +- .../CompatibilityUtil.java} | 13 +- .../service/model/LPPermissionService.java | 91 +++++++ .../sponge/service/model/LPSubject.java | 94 +++++++ .../service/model/LPSubjectCollection.java | 84 +++++++ .../sponge/service/model/LPSubjectData.java | 90 +++++++ .../persisted/PersistedCollection.java | 111 ++++++-- .../service/persisted/PersistedSubject.java | 80 +++--- .../persisted/PersistedSubjectData.java | 78 +++--- .../sponge/service/proxy/LPSubject.java | 159 ------------ .../service/proxy/LPSubjectCollection.java | 99 -------- .../sponge/service/proxy/LPSubjectData.java | 194 -------------- .../service/proxy/PermissionServiceProxy.java | 96 +++++++ .../service/proxy/SubjectCollectionProxy.java | 106 ++++++++ .../service/proxy/SubjectDataProxy.java | 205 +++++++++++++++ .../sponge/service/proxy/SubjectProxy.java | 167 ++++++++++++ .../SubjectCollectionReference.java | 60 ----- .../service/references/SubjectReference.java | 57 +++-- .../service/storage/SubjectStorage.java | 10 +- .../service/storage/SubjectStorageModel.java | 20 +- 73 files changed, 2035 insertions(+), 1254 deletions(-) create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/description/SimpleDescription.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/description/SimpleDescriptionBuilder.java rename sponge/src/main/java/me/lucko/luckperms/sponge/service/{proxy/Util.java => model/CompatibilityUtil.java} (85%) create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectCollection.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectData.java delete mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubject.java delete mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubjectCollection.java delete mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubjectData.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/PermissionServiceProxy.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectCollectionProxy.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectDataProxy.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectProxy.java delete mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectCollectionReference.java diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java index 2b5892449..e04e01ee1 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java @@ -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) { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java index 9ca2873af..b289ce4d1 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java @@ -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); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationBPermissions.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationBPermissions.java index b19f2f0dd..7fb03b631 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationBPermissions.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationBPermissions.java @@ -172,7 +172,7 @@ public class MigrationBPermissions extends SubCommand { // 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); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationGroupManager.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationGroupManager.java index 4988c95ed..321cd4239 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationGroupManager.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationGroupManager.java @@ -261,7 +261,7 @@ public class MigrationGroupManager extends SubCommand { AtomicInteger userCount = new AtomicInteger(0); for (Map.Entry> 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); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsEx.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsEx.java index 4084f3f9a..f95641d34 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsEx.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsEx.java @@ -191,7 +191,7 @@ public class MigrationPermissionsEx extends SubCommand { } 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)) { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPowerfulPerms.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPowerfulPerms.java index 6b0b28764..155d86a69 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPowerfulPerms.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPowerfulPerms.java @@ -230,7 +230,7 @@ public class MigrationPowerfulPerms extends SubCommand { // 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 permissions = joinFuture(pm.getPlayerOwnPermissions(uuid)); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationZPermissions.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationZPermissions.java index d85fd2142..bb3304b37 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationZPermissions.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationZPermissions.java @@ -133,7 +133,7 @@ public class MigrationZPermissions extends SubCommand { } 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))); diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java index c54878582..23082f02e 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java @@ -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; } diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/migration/MigrationBungeePerms.java b/bungee/src/main/java/me/lucko/luckperms/bungee/migration/MigrationBungeePerms.java index 6322f9264..45ab38e84 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/migration/MigrationBungeePerms.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/migration/MigrationBungeePerms.java @@ -160,7 +160,7 @@ public class MigrationBungeePerms extends SubCommand { // 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()) { diff --git a/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java index 5d03c7b69..edecf304d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java @@ -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(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java index d9bac383c..6bc2d71ce 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java @@ -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); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java index cba45f98f..ceb2d691a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java @@ -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); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserMainCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserMainCommand.java index 9c281e116..f0b6551af 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserMainCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserMainCommand.java @@ -87,7 +87,7 @@ public class UserMainCommand extends MainCommand { 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; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/utils/ArgumentUtils.java b/common/src/main/java/me/lucko/luckperms/common/commands/utils/ArgumentUtils.java index ebd8f8f9c..57e215ee1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/utils/ArgumentUtils.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/utils/ArgumentUtils.java @@ -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 args) { + public static ImmutableContextSet handleContexts(int fromIndex, List args) { if (args.size() <= fromIndex) { - return ContextSet.empty(); + return ImmutableContextSet.empty(); } MutableContextSet contextSet = MutableContextSet.create(); diff --git a/common/src/main/java/me/lucko/luckperms/common/data/Exporter.java b/common/src/main/java/me/lucko/luckperms/common/data/Exporter.java index 771871927..119b48ffe 100644 --- a/common/src/main/java/me/lucko/luckperms/common/data/Exporter.java +++ b/common/src/main/java/me/lucko/luckperms/common/data/Exporter.java @@ -216,7 +216,7 @@ public class Exporter implements Runnable { List 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; diff --git a/common/src/main/java/me/lucko/luckperms/common/managers/Manager.java b/common/src/main/java/me/lucko/luckperms/common/managers/Manager.java index a42ec6255..0fb23dd18 100644 --- a/common/src/main/java/me/lucko/luckperms/common/managers/Manager.java +++ b/common/src/main/java/me/lucko/luckperms/common/managers/Manager.java @@ -48,6 +48,8 @@ public interface Manager> extends Function { /** * Gets or creates an object by id * + *

Should only every be called by the storage implementation.

+ * * @param id The id to search by * @return a {@link T} object if the object is loaded or makes and returns a new object */ diff --git a/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java b/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java index 9796eb826..2af387727 100644 --- a/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java @@ -47,7 +47,7 @@ public interface UserManager extends Manager { * @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. diff --git a/common/src/main/java/me/lucko/luckperms/common/managers/impl/GenericUserManager.java b/common/src/main/java/me/lucko/luckperms/common/managers/impl/GenericUserManager.java index 92a45d7b2..dfc7ab594 100644 --- a/common/src/main/java/me/lucko/luckperms/common/managers/impl/GenericUserManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/managers/impl/GenericUserManager.java @@ -123,7 +123,7 @@ public class GenericUserManager extends AbstractManager 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 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); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/AbstractStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/AbstractStorage.java index 519198be4..f34be3457 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/AbstractStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/AbstractStorage.java @@ -104,7 +104,7 @@ public class AbstractStorage implements Storage { public CompletableFuture 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; diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/FlatfileBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/FlatfileBacking.java index 574a0e12f..901835443 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/FlatfileBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/FlatfileBacking.java @@ -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"); diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java b/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java index 86731e341..359fca59e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java +++ b/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java @@ -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(); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java index ffcedcb6b..f4c062c27 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java @@ -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 getExtraInfo() { LinkedHashMap 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 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(); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCalculatorFactory.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCalculatorFactory.java index f185a5f55..d6a8f06be 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCalculatorFactory.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCalculatorFactory.java @@ -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())); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java index 94f1a2b94..6bb59395a 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java @@ -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())); } }); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/DefaultsProcessor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/DefaultsProcessor.java index ed08e8a39..83200584e 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/DefaultsProcessor.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/DefaultsProcessor.java @@ -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; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionClear.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionClear.java index 51c71e145..1eb5b6fd3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionClear.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionClear.java @@ -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 { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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."); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionInfo.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionInfo.java index f30925668..282cb9781 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionInfo.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionInfo.java @@ -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 { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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> options = subjectData.getOptions(); + Map> options = subjectData.getAllOptions(); if (options.isEmpty()) { Util.sendPluginMessage(sender, "That subject does not have any options defined."); return CommandResult.SUCCESS; } - for (Map.Entry> e : options.entrySet()) { + for (Map.Entry> e : options.entrySet()) { Util.sendPluginMessage(sender, "&3>> &bContext: " + SpongeUtils.contextToString(e.getKey()) + "\n" + SpongeUtils.optionsToString(e.getValue())); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionSet.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionSet.java index 82cb03d37..94c98b704 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionSet.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionSet.java @@ -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 { public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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?"); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionUnset.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionUnset.java index b97d0c982..818f04913 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionUnset.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/OptionUnset.java @@ -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 { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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?"); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java index a96300b5e..4574db0af 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java @@ -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 { public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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?"); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentClear.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentClear.java index c31a76c8c..56bb4d073 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentClear.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentClear.java @@ -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 { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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."); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentInfo.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentInfo.java index 69a8274cb..704dc20fa 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentInfo.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentInfo.java @@ -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 { public ParentInfo() { @@ -53,21 +53,21 @@ public class ParentInfo extends SubCommand { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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> parents = subjectData.getParents(); + Map> parents = subjectData.getAllParents(); if (parents.isEmpty()) { Util.sendPluginMessage(sender, "That subject does not have any parents defined."); return CommandResult.SUCCESS; } - for (Map.Entry> e : parents.entrySet()) { + for (Map.Entry> e : parents.entrySet()) { Util.sendPluginMessage(sender, "&3>> &bContext: " + SpongeUtils.contextToString(e.getKey()) + "\n" + SpongeUtils.parentsToString(e.getValue())); } } else { - Set parents = subjectData.getParents(contextSet); + List parents = subjectData.getParents(contextSet); if (parents.isEmpty()) { Util.sendPluginMessage(sender, "That subject does not have any parents defined in those contexts."); return CommandResult.SUCCESS; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java index 8bfa9b352..7e342b942 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java @@ -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 { public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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?"); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionClear.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionClear.java index 064474c82..9954d9b12 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionClear.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionClear.java @@ -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 { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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."); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionInfo.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionInfo.java index cb38ad7f3..299e49317 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionInfo.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionInfo.java @@ -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 { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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> permissions = subjectData.getPermissions(); + Map> permissions = subjectData.getAllPermissions(); if (permissions.isEmpty()) { Util.sendPluginMessage(sender, "That subject does not have any permissions defined."); return CommandResult.SUCCESS; } - for (Map.Entry> e : permissions.entrySet()) { + for (Map.Entry> e : permissions.entrySet()) { Util.sendPluginMessage(sender, "&3>> &bContext: " + SpongeUtils.contextToString(e.getKey()) + "\n" + SpongeUtils.nodesToString(e.getValue())); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionSet.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionSet.java index 9848f36b2..371776f33 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionSet.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionSet.java @@ -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 { public CommandResult execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subjectData, List 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?"); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeMainCommand.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeMainCommand.java index 3e883eab5..8a9fe73c6 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeMainCommand.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeMainCommand.java @@ -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 { 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 { 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 subjects = collection.getSubjects().stream() + List subjects = collection.getLoadedSubjects().stream() .map(LPSubject::getIdentifier) .collect(Collectors.toList()); @@ -183,11 +183,11 @@ public class SpongeMainCommand extends Command { } 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; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeUtils.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeUtils.java index 8d47e6e6e..7daa416a4 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeUtils.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeUtils.java @@ -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 parents) { + public static String parentsToString(Iterable parents) { StringBuilder sb = new StringBuilder(); for (SubjectReference s : parents) { sb.append("&3> &a") diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/contexts/SpongeCalculatorLink.java b/sponge/src/main/java/me/lucko/luckperms/sponge/contexts/SpongeCalculatorLink.java index ca60209c6..3c7bbfd0b 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/contexts/SpongeCalculatorLink.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/contexts/SpongeCalculatorLink.java @@ -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 { Set contexts = new HashSet<>(); calculator.accumulateContexts(subject, contexts); - accumulator.addAll(Util.convertContexts(contexts)); + accumulator.addAll(CompatibilityUtil.convertContexts(contexts)); return accumulator; } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeGroupManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeGroupManager.java index d3e1fc8bc..2d6f39547 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeGroupManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeGroupManager.java @@ -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 objects = Caffeine.newBuilder() .build(new CacheLoader() { @Override @@ -77,20 +86,26 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection { private final LoadingCache 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 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 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 getSubject(String identifier) { + return Optional.ofNullable(getIfLoaded(identifier.toLowerCase())).map(SpongeGroup::sponge); + } + + @Override + public CompletableFuture 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> loadSubjects(Set identifiers) { + return CompletableFuture.supplyAsync(() -> { + ImmutableSet.Builder ret = ImmutableSet.builder(); + for (String id : identifiers) { + ret.add(loadSubject(id.toLowerCase()).join()); + } + + return ret.build(); + }, plugin.getScheduler().getAsyncExecutor()); } @Override - public Collection getSubjects() { - return objects.asMap().values().stream().map(SpongeGroup::getSpongeData).collect(ImmutableCollectors.toImmutableList()); + public ImmutableCollection getLoadedSubjects() { + return getAll().values().stream().map(SpongeGroup::sponge).collect(ImmutableCollectors.toImmutableSet()); } @Override - public Map getWithPermission(@NonNull ContextSet contexts, @NonNull String node) { + public CompletableFuture> getAllIdentifiers() { + return CompletableFuture.completedFuture(ImmutableSet.copyOf(getAll().keySet())); + } + + @Override + public CompletableFuture> getAllWithPermission(String permission) { + return CompletableFuture.supplyAsync(() -> { + ImmutableMap.Builder ret = ImmutableMap.builder(); + + List> lookup = plugin.getStorage().getGroupsWithPermission(permission).join(); + for (HeldPermission 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> getAllWithPermission(ImmutableContextSet contexts, String permission) { + return CompletableFuture.supplyAsync(() -> { + ImmutableMap.Builder ret = ImmutableMap.builder(); + + List> lookup = plugin.getStorage().getGroupsWithPermission(permission).join(); + for (HeldPermission 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 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 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 } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java index 17c3499f1..70ccd0cf7 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java @@ -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 objects = Caffeine.newBuilder() .build(new CacheLoader() { @Override @@ -82,23 +87,29 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection { private final LoadingCache subjectLoadingCache = Caffeine.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 set = new HashSet<>(); - for (Map.Entry 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 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 loadSubject(String identifier) { + UUID uuid; + try { + uuid = UUID.fromString(identifier); + } catch (IllegalArgumentException e) { + CompletableFuture 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 getSubject(String identifier) { + UUID uuid = UUID.fromString(identifier); + return Optional.ofNullable(getIfLoaded(uuid)).map(SpongeUser::sponge); + } + + @Override + public CompletableFuture 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 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 getSubjects() { - return objects.asMap().values().stream().map(SpongeUser::getSpongeData).collect(ImmutableCollectors.toImmutableList()); + public CompletableFuture> loadSubjects(Set identifiers) { + return CompletableFuture.supplyAsync(() -> { + ImmutableSet.Builder 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 getWithPermission(@NonNull ContextSet contexts, @NonNull String node) { + public ImmutableCollection getLoadedSubjects() { + return getAll().values().stream().map(SpongeUser::sponge).collect(ImmutableCollectors.toImmutableSet()); + } + + @Override + public CompletableFuture> getAllIdentifiers() { + return CompletableFuture.supplyAsync(() -> { + ImmutableSet.Builder 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> getAllWithPermission(String permission) { + return CompletableFuture.supplyAsync(() -> { + ImmutableMap.Builder ret = ImmutableMap.builder(); + + List> lookup = plugin.getStorage().getUsersWithPermission(permission).join(); + for (HeldPermission 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> getAllWithPermission(ImmutableContextSet contexts, String permission) { + return CompletableFuture.supplyAsync(() -> { + ImmutableMap.Builder ret = ImmutableMap.builder(); + + List> lookup = plugin.getStorage().getUsersWithPermission(permission).join(); + for (HeldPermission 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 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 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 + } + } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionManager.java index 3efbe8fe1..9b0407a07 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionManager.java @@ -93,10 +93,10 @@ public class MigrationPermissionManager extends SubCommand { 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 { // 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); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionsEx.java b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionsEx.java index 804fdf123..a1270d0ae 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionsEx.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/MigrationPermissionsEx.java @@ -90,10 +90,10 @@ public class MigrationPermissionsEx extends SubCommand { 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 { // 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); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/SpongeMigrationUtils.java b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/SpongeMigrationUtils.java index 1e952e342..f11a10242 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/SpongeMigrationUtils.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/SpongeMigrationUtils.java @@ -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, Map> perms = subject.getSubjectData().getAllPermissions(); for (Map.Entry, Map> e : perms.entrySet()) { - ContextSet context = Util.convertContexts(e.getKey()); + ContextSet context = CompatibilityUtil.convertContexts(e.getKey()); for (Map.Entry perm : e.getValue().entrySet()) { if (perm.getKey().isEmpty()) { @@ -69,7 +69,7 @@ public class SpongeMigrationUtils { // Migrate options Map, Map> opts = subject.getSubjectData().getAllOptions(); for (Map.Entry, Map> e : opts.entrySet()) { - ContextSet context = Util.convertContexts(e.getKey()); + ContextSet context = CompatibilityUtil.convertContexts(e.getKey()); for (Map.Entry opt : e.getValue().entrySet()) { if (opt.getKey().isEmpty() || opt.getValue().isEmpty()) { @@ -89,7 +89,7 @@ public class SpongeMigrationUtils { // Migrate parents Map, List> parents = subject.getSubjectData().getAllParents(); for (Map.Entry, List> 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)) { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java index 31248c793..4b322c2f2 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java @@ -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 permissionCache = Caffeine.newBuilder() + private final LoadingCache 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> parentCache = Caffeine.newBuilder() + private final LoadingCache> parentCache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .build(contexts -> { Set 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 getParents(ContextSet contexts) { + public ImmutableList getParents(ImmutableContextSet contexts) { try (Timing ignored = plugin.getTimings().time(LPTiming.GROUP_GET_PARENTS)) { return parentCache.get(contexts); } } @Override - public Optional getOption(ContextSet contexts, String s) { + public Optional getOption(ImmutableContextSet contexts, String s) { try (Timing ignored = plugin.getService().getPlugin().getTimings().time(LPTiming.GROUP_GET_OPTION)) { Optional 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 getChatMeta(ContextSet contexts, boolean prefix) { + private Optional 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 getMeta(ContextSet contexts, String key) { + private Optional getMeta(ImmutableContextSet contexts, String key) { MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, ExtractedContexts.generate(plugin.getService().calculateContexts(contexts))); Map meta = metaAccumulator.getMeta(); return Optional.ofNullable(meta.get(key)); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java index d5123a97f..ba07b2ca3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java @@ -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 getParents(ContextSet contexts) { - logUsage(); + public ImmutableList getParents(ImmutableContextSet contexts) { try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PARENTS)) { ImmutableSet.Builder 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 getOption(ContextSet contexts, String s) { - logUsage(); + public Optional 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 v = plugin.getService().getUserSubjects().getDefaultSubject().resolve(getService()).getOption(contexts, s); + Optional 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(); } } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java index a671de59b..ca4358eb4 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java @@ -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 descriptionSet; private final Set> localPermissionCaches; - private final Set>> localParentCaches; + private final Set>> localParentCaches; private final Set>> localOptionCaches; private final Set localDataCaches; @@ -114,7 +109,7 @@ public class LuckPermsService implements PermissionService { .build(new CacheLoader() { @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 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 getCollections() { + @Override + public ImmutableMap getLoadedCollections() { return ImmutableMap.copyOf(collections.asMap()); } - @Deprecated @Override - public Map 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 newDescriptionBuilder(@NonNull Object o) { + public PermissionDescription.Builder newDescriptionBuilder(@NonNull Object o) { Optional 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 getDescriptions() { + public ImmutableSet getDescriptions() { return ImmutableSet.copyOf(descriptionSet); } @Override public void registerContextCalculator(@NonNull ContextCalculator contextCalculator) { - plugin.getContextManager().registerCalculator(new SpongeCalculatorLink(contextCalculator)); + plugin.getContextManager().registerCalculator(contextCalculator); } - public List sortSubjects(List s) { - List ret = new ArrayList<>(s); + @Override + public ImmutableList sortSubjects(Collection s) { + List 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 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> c : localParentCaches) { + for (LoadingCache> c : localParentCaches) { c.invalidateAll(); } invalidateOptionCaches(); invalidatePermissionCaches(); } + @Override public void invalidateOptionCaches() { for (LoadingCache> 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 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 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 getAssignedSubjects(String id) { - SubjectCollection subjects = service.getSubjects(id); - return subjects.getAllWithPermission(this.id); - } - } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java index 64e5a8c41..030f7e4bb 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java @@ -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> getPermissions() { + public ImmutableMap> getAllPermissions() { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) { Map> perms = new HashMap<>(); @@ -86,7 +85,7 @@ public class LuckPermsSubjectData implements LPSubjectData { perms.put(e.getKey(), results); } - ImmutableMap.Builder> map = ImmutableMap.builder(); + ImmutableMap.Builder> map = ImmutableMap.builder(); for (Map.Entry> 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 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 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 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> getParents() { + public ImmutableMap> getAllParents() { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PARENTS)) { - Map> parents = new HashMap<>(); + Map> parents = new HashMap<>(); for (Map.Entry> e : (enduring ? holder.getNodes() : holder.getTransientNodes()).asMap().entrySet()) { - ImmutableSet.Builder results = ImmutableSet.builder(); + ImmutableList.Builder 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> map = ImmutableMap.builder(); - for (Map.Entry> e : parents.entrySet()) { + ImmutableMap.Builder> map = ImmutableMap.builder(); + for (Map.Entry> 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 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 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 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 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> getOptions() { + public ImmutableMap> getAllOptions() { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_OPTIONS)) { Map> options = new HashMap<>(); Map minPrefixPriority = new HashMap<>(); @@ -363,7 +356,7 @@ public class LuckPermsSubjectData implements LPSubjectData { } } - ImmutableMap.Builder> map = ImmutableMap.builder(); + ImmutableMap.Builder> map = ImmutableMap.builder(); for (Map.Entry> 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 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 unsetOption(ImmutableContextSet set, String key) { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_OPTION)) { List 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 clearOptions(@NonNull ImmutableContextSet set) { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_OPTIONS)) { List 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 clearOptions() { try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_OPTIONS)) { List 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 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()); } } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/ServiceCacheHousekeepingTask.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/ServiceCacheHousekeepingTask.java index 4f1f605e2..afc174fe6 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/ServiceCacheHousekeepingTask.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/ServiceCacheHousekeepingTask.java @@ -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(); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java index 988255e65..bf2cd2c30 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java @@ -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> permissions = new ConcurrentHashMap<>(); + private final Map> permissions = new ConcurrentHashMap<>(); + private final Map> parents = new ConcurrentHashMap<>(); + private final Map> options = new ConcurrentHashMap<>(); + private final LoadingCache permissionCache = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.MINUTES) .build(new CacheLoader() { @@ -85,9 +91,6 @@ public class CalculatedSubjectData implements LPSubjectData { } }); - private final Map> parents = new ConcurrentHashMap<>(); - private final Map> options = new ConcurrentHashMap<>(); - public void cleanup() { permissionCache.cleanUp(); } @@ -128,210 +131,185 @@ public class CalculatedSubjectData implements LPSubjectData { } @Override - public Map> getPermissions() { - ImmutableMap.Builder> map = ImmutableMap.builder(); - for (Map.Entry> e : permissions.entrySet()) { - map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue())); + public ImmutableMap> getAllPermissions() { + ImmutableMap.Builder> map = ImmutableMap.builder(); + for (Map.Entry> e : permissions.entrySet()) { + map.put(e.getKey(), ImmutableMap.copyOf(e.getValue())); } return map.build(); } @Override - public Map getPermissions(ContextSet contexts) { - return ImmutableMap.copyOf(permissions.getOrDefault(contexts, ImmutableMap.of())); - } - - @Override - public boolean setPermission(ContextSet contexts, String permission, Tristate value) { + public CompletableFuture setPermission(ImmutableContextSet contexts, String permission, Tristate value) { boolean b; if (value == Tristate.UNDEFINED) { Map perms = permissions.get(contexts); b = perms != null && perms.remove(permission.toLowerCase()) != null; } else { - Map perms = permissions.computeIfAbsent(contexts.makeImmutable(), c -> new ConcurrentHashMap<>()); + Map 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 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 clearPermissions(ImmutableContextSet contexts) { Map 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> getParents() { - ImmutableMap.Builder> map = ImmutableMap.builder(); - for (Map.Entry> e : parents.entrySet()) { - map.put(e.getKey().makeImmutable(), ImmutableSet.copyOf(e.getValue())); - } - return map.build(); - } - - public Map> getParentsAsList() { - ImmutableMap.Builder> map = ImmutableMap.builder(); - for (Map.Entry> e : parents.entrySet()) { - map.put(e.getKey().makeImmutable(), ImmutableList.copyOf(e.getValue())); + public ImmutableMap> getAllParents() { + ImmutableMap.Builder> map = ImmutableMap.builder(); + for (Map.Entry> e : parents.entrySet()) { + map.put(e.getKey(), service.sortSubjects(e.getValue())); } return map.build(); } @Override - public Set getParents(ContextSet contexts) { - return ImmutableSet.copyOf(parents.getOrDefault(contexts, ImmutableSet.of())); - } - - @Override - public boolean addParent(ContextSet contexts, SubjectReference parent) { - Set set = parents.computeIfAbsent(contexts.makeImmutable(), c -> ConcurrentHashMap.newKeySet()); + public CompletableFuture addParent(ImmutableContextSet contexts, SubjectReference parent) { + Set 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 removeParent(ImmutableContextSet contexts, SubjectReference parent) { Set 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 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 clearParents(ImmutableContextSet contexts) { Set 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> getOptions() { - ImmutableMap.Builder> map = ImmutableMap.builder(); - for (Map.Entry> e : options.entrySet()) { - map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue())); + public ImmutableMap> getAllOptions() { + ImmutableMap.Builder> map = ImmutableMap.builder(); + for (Map.Entry> e : options.entrySet()) { + map.put(e.getKey(), ImmutableMap.copyOf(e.getValue())); } return map.build(); } @Override - public Map getOptions(ContextSet contexts) { - return ImmutableMap.copyOf(options.getOrDefault(contexts, ImmutableMap.of())); - } - - @Override - public boolean setOption(ContextSet contexts, String key, String value) { - Map options = this.options.computeIfAbsent(contexts.makeImmutable(), c -> new ConcurrentHashMap<>()); + public CompletableFuture setOption(ImmutableContextSet contexts, String key, String value) { + Map 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 unsetOption(ImmutableContextSet contexts, String key) { Map 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 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 clearOptions(ImmutableContextSet contexts) { Map 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 Map flattenMap(ContextSet contexts, Map> source) { + private static Map flattenMap(ContextSet contexts, Map> source) { Map map = new HashMap<>(); - SortedMap> ret = getRelevantEntries(contexts, source); + SortedMap> ret = getRelevantEntries(contexts, source); for (Map m : ret.values()) { for (Map.Entry 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 SortedMap> getRelevantEntries(ContextSet set, Map> map) { - ImmutableSortedMap.Builder> perms = ImmutableSortedMap.orderedBy(CONTEXT_COMPARATOR); + private static SortedMap> getRelevantEntries(ContextSet set, Map> map) { + ImmutableSortedMap.Builder> perms = ImmutableSortedMap.orderedBy(CONTEXT_COMPARATOR); - for (Map.Entry> e : map.entrySet()) { + for (Map.Entry> 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 { + private static class ContextComparator implements Comparator { @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; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/description/SimpleDescription.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/description/SimpleDescription.java new file mode 100644 index 000000000..8851eefb9 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/description/SimpleDescription.java @@ -0,0 +1,70 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 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(); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/description/SimpleDescriptionBuilder.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/description/SimpleDescriptionBuilder.java new file mode 100644 index 000000000..80d336d35 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/description/SimpleDescriptionBuilder.java @@ -0,0 +1,103 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 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 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; + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/legacystorage/LegacyDataMigrator.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/legacystorage/LegacyDataMigrator.java index 07ac3a41e..40b01c6fc 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/legacystorage/LegacyDataMigrator.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/legacystorage/LegacyDataMigrator.java @@ -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(); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/legacystorage/SubjectDataHolder.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/legacystorage/SubjectDataHolder.java index d08469108..f12c2e108 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/legacystorage/SubjectDataHolder.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/legacystorage/SubjectDataHolder.java @@ -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()) )) ); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/Util.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/CompatibilityUtil.java similarity index 85% rename from sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/Util.java rename to sponge/src/main/java/me/lucko/luckperms/sponge/service/model/CompatibilityUtil.java index a2206e0fc..d5ed7a74b 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/Util.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/CompatibilityUtil.java @@ -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, ImmutableContextSet> SPONGE_TO_LP_CACHE = Caffeine.newBuilder() .build(ImmutableContextSet::fromEntries); - private static final LoadingCache> LP_TO_SPONGE_CACHE = Caffeine.newBuilder() + private static final LoadingCache> 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 contexts) { + public static ImmutableContextSet convertContexts(@NonNull Set contexts) { return SPONGE_TO_LP_CACHE.get(ImmutableSet.copyOf(contexts)); } - public static Set convertContexts(@NonNull ContextSet contexts) { + public static ImmutableSet convertContexts(@NonNull ContextSet contexts) { return LP_TO_SPONGE_CACHE.get(contexts.makeImmutable()); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java new file mode 100644 index 000000000..04a815091 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java @@ -0,0 +1,91 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 getIdentifierValidityPredicate(); + + LPSubjectCollection getCollection(String identifier); + + ImmutableMap getLoadedCollections(); + + SubjectReference newSubjectReference(String collectionIdentifier, String subjectIdentifier); + + PermissionDescription.Builder newDescriptionBuilder(Object plugin); + + Optional getDescription(String permission); + + ImmutableCollection getDescriptions(); + + void registerContextCalculator(ContextCalculator calculator); + + // utils + ImmutableList sortSubjects(Collection s); + + Contexts calculateContexts(ImmutableContextSet contextSet); + + void invalidatePermissionCaches(); + + void invalidateParentCaches(); + + void invalidateOptionCaches(); +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java new file mode 100644 index 000000000..085d8b0d5 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java @@ -0,0 +1,94 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 getFriendlyIdentifier() { + return Optional.empty(); + } + + default Optional getCommandSource() { + return Optional.empty(); + } + + LPSubjectCollection getParentCollection(); + + LPSubjectData getSubjectData(); + + LPSubjectData getTransientSubjectData(); + + Tristate getPermissionValue(ImmutableContextSet contexts, String permission); + + boolean isChildOf(ImmutableContextSet contexts, SubjectReference parent); + + ImmutableList getParents(ImmutableContextSet contexts); + + Optional getOption(ImmutableContextSet contexts, String key); + + ImmutableContextSet getActiveContextSet(); + + default void performCleanup() { + + } + +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectCollection.java new file mode 100644 index 000000000..72473db38 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectCollection.java @@ -0,0 +1,84 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 getIdentifierValidityPredicate(); + + CompletableFuture loadSubject(String identifier); + + Optional getSubject(String identifier); + + CompletableFuture hasRegistered(String identifier); + + CompletableFuture> loadSubjects(Set identifiers); + + ImmutableCollection getLoadedSubjects(); + + CompletableFuture> getAllIdentifiers(); + + CompletableFuture> getAllWithPermission(String permission); + + CompletableFuture> getAllWithPermission(ImmutableContextSet contexts, String permission); + + ImmutableMap getLoadedWithPermission(String permission); + + ImmutableMap getLoadedWithPermission(ImmutableContextSet contexts, String permission); + + LPSubject getDefaults(); + + void suggestUnload(String identifier); + +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectData.java new file mode 100644 index 000000000..bd4be7bef --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectData.java @@ -0,0 +1,90 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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> getAllPermissions(); + + default ImmutableMap getPermissions(ImmutableContextSet contexts) { + return ImmutableMap.copyOf(getAllPermissions().getOrDefault(contexts, ImmutableMap.of())); + } + + CompletableFuture setPermission(ImmutableContextSet contexts, String permission, Tristate value); + + CompletableFuture clearPermissions(); + + CompletableFuture clearPermissions(ImmutableContextSet contexts); + + /* parents */ + + ImmutableMap> getAllParents(); + + default ImmutableList getParents(ImmutableContextSet contexts) { + return ImmutableList.copyOf(getAllParents().getOrDefault(contexts, ImmutableList.of())); + } + + CompletableFuture addParent(ImmutableContextSet contexts, SubjectReference parent); + + CompletableFuture removeParent(ImmutableContextSet contexts, SubjectReference parent); + + CompletableFuture clearParents(); + + CompletableFuture clearParents(ImmutableContextSet contexts); + + /* options */ + + ImmutableMap> getAllOptions(); + + default ImmutableMap getOptions(ImmutableContextSet contexts) { + return ImmutableMap.copyOf(getAllOptions().getOrDefault(contexts, ImmutableMap.of())); + } + + CompletableFuture setOption(ImmutableContextSet contexts, String key, String value); + + CompletableFuture unsetOption(ImmutableContextSet contexts, String key); + + CompletableFuture clearOptions(); + + CompletableFuture clearOptions(ImmutableContextSet contexts); + +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedCollection.java index cbe2840d3..cfcda89aa 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedCollection.java @@ -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 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 holders = service.getStorage().loadAllFromFile(identifier); for (Map.Entry 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 getIdentifierValidityPredicate() { + return Predicates.alwaysTrue(); } @Override - public Collection getSubjects() { - return subjects.asMap().values().stream().map(s -> (LPSubject) s).collect(ImmutableCollectors.toImmutableList()); + public CompletableFuture loadSubject(String identifier) { + return CompletableFuture.completedFuture(subjects.get(identifier.toLowerCase())); } @Override - public Map getWithPermission(@NonNull ContextSet contexts, @NonNull String node) { + public Optional getSubject(String identifier) { + return Optional.of(subjects.get(identifier.toLowerCase())); + } + + @Override + public CompletableFuture hasRegistered(String identifier) { + return CompletableFuture.completedFuture(subjects.asMap().containsKey(identifier.toLowerCase())); + } + + @Override + public CompletableFuture> loadSubjects(Set identifiers) { + ImmutableSet.Builder ret = ImmutableSet.builder(); + for (String id : identifiers) { + ret.add(subjects.get(id.toLowerCase())); + } + return CompletableFuture.completedFuture(ret.build()); + } + + @Override + public ImmutableCollection getLoadedSubjects() { + return ImmutableList.copyOf(subjects.asMap().values()); + } + + @Override + public CompletableFuture> getAllIdentifiers() { + return CompletableFuture.completedFuture(ImmutableSet.copyOf(subjects.asMap().keySet())); + } + + @Override + public CompletableFuture> getAllWithPermission(String permission) { + return CompletableFuture.completedFuture(getLoadedWithPermission(permission).entrySet().stream() + .collect(ImmutableCollectors.toImmutableMap(e -> e.getKey().toReference(), Map.Entry::getValue))); + } + + @Override + public CompletableFuture> 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 getLoadedWithPermission(String permission) { ImmutableMap.Builder 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 getLoadedWithPermission(ImmutableContextSet contexts, String permission) { + ImmutableMap.Builder 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 } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java index 8f51e192a..872ed6ad5 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java @@ -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> parentLookupCache = Caffeine.newBuilder() + private final LoadingCache> 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 lookupParents(ContextSet contexts) { - Set s = new HashSet<>(); + private ImmutableList lookupParents(ImmutableContextSet contexts) { + List 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 lookupOptionValue(ContextSet contexts, String key) { + private Optional lookupOptionValue(ImmutableContextSet contexts, String key) { Optional 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 getParents(@NonNull ContextSet contexts) { + public ImmutableList 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 getOption(ContextSet contexts, String key) { + public Optional 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(); } } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubjectData.java index 7b45d0ba6..bc3f94a06 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubjectData.java @@ -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 { 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 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 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 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 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 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 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 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 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 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 clearOptions() { + return super.clearOptions().thenApply(this); + } + + @Override + public CompletableFuture clearOptions(ImmutableContextSet contexts) { + return super.clearOptions(contexts).thenApply(this); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubject.java deleted file mode 100644 index 002ec06cc..000000000 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubject.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * 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 getFriendlyIdentifier() { - return Optional.empty(); - } - - @Override - default Optional 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 getParents(ContextSet contexts); - - Optional getOption(ContextSet contexts, String key); - - ContextSet getActiveContextSet(); - - - /* Compat */ - - @Override - default LPSubjectCollection getContainingCollection() { - return getParentCollection().resolve(getService()); - } - - @Deprecated - @Override - default boolean hasPermission(@NonNull Set 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 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 contexts, @NonNull Subject parent) { - return isChildOf(convertContexts(contexts), SubjectReference.of(parent)); - } - - @Deprecated - @Override - default List getParents() { - List ret = getParents(getActiveContextSet()).stream().map(s -> s.resolve(getService())).collect(ImmutableCollectors.toImmutableList()); - return getService().sortSubjects(ret); - } - - @Deprecated - @Override - default List getParents(@NonNull Set contexts) { - List ret = getParents(convertContexts(contexts)).stream().map(s -> s.resolve(getService())).collect(ImmutableCollectors.toImmutableList()); - return getService().sortSubjects(ret); - } - - @Deprecated - @Override - default Optional getOption(@NonNull String key) { - return getOption(getActiveContextSet(), key); - } - - @Deprecated - @Override - default Optional getOption(@NonNull Set contexts, @NonNull String key) { - return getOption(convertContexts(contexts), key); - } - - @Deprecated - @Override - default Set getActiveContexts() { - return convertContexts(getActiveContextSet()); - } -} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubjectCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubjectCollection.java deleted file mode 100644 index dcf013415..000000000 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubjectCollection.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * 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 getSubjects(); - - default Map getWithPermission(String permission) { - return getWithPermission(ContextSet.empty(), permission); - } - - Map getWithPermission(ContextSet contexts, String permission); - - SubjectReference getDefaultSubject(); - - boolean getTransientHasPriority(); - - @Deprecated - @Override - default Subject getDefaults() { - return getDefaultSubject().resolve(getService()); - } - - @Deprecated - @Override - default Iterable getAllSubjects() { - return getSubjects().stream().collect(ImmutableCollectors.toImmutableList()); - } - - @Deprecated - @Override - default Map getAllWithPermission(@NonNull String permission) { - return getWithPermission(permission).entrySet().stream().collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - @Deprecated - @Override - default Map getAllWithPermission(@NonNull Set contexts, @NonNull String permission) { - return getWithPermission(convertContexts(contexts), permission).entrySet().stream().collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); - } - -} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubjectData.java deleted file mode 100644 index 36398f2cf..000000000 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/LPSubjectData.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * 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> getPermissions(); - - default Map 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> getParents(); - - default Set 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> getOptions(); - - default Map 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, Map> getAllPermissions() { - return getPermissions().entrySet().stream() - .collect(ImmutableCollectors.toImmutableMap( - e -> convertContexts(e.getKey()), - e -> ImmutableMap.copyOf(e.getValue())) - ); - } - - @Deprecated - @Override - default Map getPermissions(Set contexts) { - return ImmutableMap.copyOf(getPermissions(convertContexts(contexts))); - } - - @Deprecated - @Override - default boolean setPermission(Set contexts, String permission, Tristate value) { - return setPermission(convertContexts(contexts), permission, convertTristate(value)); - } - - @Deprecated - @Override - default boolean clearPermissions(Set contexts) { - return clearPermissions(convertContexts(contexts)); - } - - @Deprecated - @Override - default Map, List> 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 getParents(Set contexts) { - return getParents(convertContexts(contexts)).stream().map(s -> s.resolve(getParentSubject().getService())).collect(ImmutableCollectors.toImmutableList()); - } - - @Deprecated - @Override - default boolean addParent(Set contexts, Subject parent) { - return addParent(convertContexts(contexts), SubjectReference.of(parent)); - } - - @Deprecated - @Override - default boolean removeParent(Set contexts, Subject parent) { - return removeParent(convertContexts(contexts), SubjectReference.of(parent)); - } - - @Deprecated - @Override - default boolean clearParents(Set contexts) { - return clearParents(convertContexts(contexts)); - } - - @Deprecated - @Override - default Map, Map> getAllOptions() { - return getOptions().entrySet().stream() - .collect(ImmutableCollectors.toImmutableMap( - e -> convertContexts(e.getKey()), - e -> ImmutableMap.copyOf(e.getValue())) - ); - } - - @Deprecated - @Override - default Map getOptions(Set contexts) { - return ImmutableMap.copyOf(getOptions(convertContexts(contexts))); - } - - @Deprecated - @Override - default boolean setOption(Set contexts, String key, @Nullable String value) { - return value == null ? unsetOption(convertContexts(contexts), key) : setOption(convertContexts(contexts), key, value); - } - - @Deprecated - @Override - default boolean clearOptions(Set contexts) { - return clearOptions(convertContexts(contexts)); - } - -} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/PermissionServiceProxy.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/PermissionServiceProxy.java new file mode 100644 index 000000000..e8b07bf14 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/PermissionServiceProxy.java @@ -0,0 +1,96 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 getKnownSubjects() { + return handle.getLoadedCollections().entrySet().stream() + .collect(ImmutableCollectors.toImmutableMap( + Map.Entry::getKey, + e -> e.getValue().sponge() + )); + } + + @Override + public Optional newDescriptionBuilder(Object o) { + return Optional.of(handle.newDescriptionBuilder(o)); + } + + @Override + public Optional getDescription(String s) { + return handle.getDescription(s); + } + + @Override + public Collection getDescriptions() { + return handle.getDescriptions(); + } + + @Override + public void registerContextCalculator(ContextCalculator contextCalculator) { + handle.registerContextCalculator(new SpongeCalculatorLink(contextCalculator)); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectCollectionProxy.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectCollectionProxy.java new file mode 100644 index 000000000..dd0949a62 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectCollectionProxy.java @@ -0,0 +1,106 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 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 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 getAllWithPermission(Set 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(); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectDataProxy.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectDataProxy.java new file mode 100644 index 000000000..cdc414cc7 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectDataProxy.java @@ -0,0 +1,205 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 getHandle() { + return enduring ? ref.resolve().thenApply(LPSubject::getSubjectData) : ref.resolve().thenApply(LPSubject::getTransientSubjectData); + } + + @Override + public Map, Map> 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 getPermissions(Set contexts) { + return getHandle().thenApply(handle -> handle.getPermissions(CompatibilityUtil.convertContexts(contexts))).join(); + } + + @Override + public boolean setPermission(Set 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 contexts) { + getHandle().thenCompose(handle -> handle.clearPermissions(CompatibilityUtil.convertContexts(contexts))); + return true; + } + + @Override + public Map, List> 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 getParents(Set 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 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 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 contexts) { + getHandle().thenCompose(handle -> handle.clearParents(CompatibilityUtil.convertContexts(contexts))); + return true; + } + + @Override + public Map, Map> 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 getOptions(Set contexts) { + return getHandle().thenApply(handle -> handle.getOptions(CompatibilityUtil.convertContexts(contexts))).join(); + } + + @Override + public boolean setOption(Set 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 contexts) { + getHandle().thenCompose(handle -> handle.clearOptions(CompatibilityUtil.convertContexts(contexts))); + return true; + } + + @Override + public boolean clearOptions() { + getHandle().thenCompose(LPSubjectData::clearOptions); + return true; + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectProxy.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectProxy.java new file mode 100644 index 000000000..0259dea8c --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/proxy/SubjectProxy.java @@ -0,0 +1,167 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 getHandle() { + return ref.resolve(); + } + + @Override + public Optional 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 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 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 contexts, Subject parent) { + return getHandle().thenApply(handle -> { + return handle.isChildOf( + CompatibilityUtil.convertContexts(contexts), + service.newSubjectReference( + parent.getContainingCollection().getIdentifier(), + parent.getIdentifier() + ) + ); + }).join(); + } + + @Override + public List 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 getParents(Set 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 getOption(Set contexts, String key) { + return getHandle().thenApply(handle -> { + return handle.getOption(CompatibilityUtil.convertContexts(contexts), key); + }).join(); + } + + @Override + public Optional getOption(String key) { + return getHandle().thenApply(handle -> { + return handle.getOption(ImmutableContextSet.empty(), key); + }).join(); + } + + @Override + public String getIdentifier() { + return ref.getIdentifier(); + } + + @Override + public Set getActiveContexts() { + return getHandle().thenApply(handle -> CompatibilityUtil.convertContexts(handle.getActiveContextSet())).join(); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectCollectionReference.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectCollectionReference.java deleted file mode 100644 index 3b9214e6c..000000000 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectCollectionReference.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * 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 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; - } -} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectReference.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectReference.java index 202bc5e46..24aff5ae7 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectReference.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectReference.java @@ -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 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 ref = null; + private long lastLookup = 0L; + private WeakReference 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 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); + } + } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorage.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorage.java index d954f6b53..19bae340d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorage.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorage.java @@ -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); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java index 345c9a907..bb6bd747d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java @@ -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> permissions; private final Map> options; private final Map> parents; - public SubjectStorageModel(Map> permissions, Map> options, Map> parents) { + public SubjectStorageModel(LPPermissionService service, Map> permissions, Map> options, Map> parents) { + this.service = service; + ImmutableMap.Builder> permissionsBuilder = ImmutableMap.builder(); - for (Map.Entry> e : permissions.entrySet()) { + for (Map.Entry> e : permissions.entrySet()) { permissionsBuilder.put(e.getKey(), ImmutableMap.copyOf(e.getValue())); } this.permissions = permissionsBuilder.build(); ImmutableMap.Builder> optionsBuilder = ImmutableMap.builder(); - for (Map.Entry> e : options.entrySet()) { + for (Map.Entry> e : options.entrySet()) { optionsBuilder.put(e.getKey(), ImmutableMap.copyOf(e.getValue())); } this.options = optionsBuilder.build(); ImmutableMap.Builder> parentsBuilder = ImmutableMap.builder(); - for (Map.Entry> e : parents.entrySet()) { + for (Map.Entry> 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());