diff --git a/api/src/main/java/me/lucko/luckperms/api/Contexts.java b/api/src/main/java/me/lucko/luckperms/api/Contexts.java index 7c7314ae0..af419efe7 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Contexts.java +++ b/api/src/main/java/me/lucko/luckperms/api/Contexts.java @@ -41,14 +41,18 @@ public class Contexts { * @return a context that will not apply any filters */ public static Contexts allowAll() { - return new Contexts(Collections.emptyMap(), true, true, true, true, true); + return new Contexts(Collections.emptyMap(), true, true, true, true, true, true); } public static Contexts of(Map context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) { return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups); } - public Contexts(Map context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) { + public static Contexts of(Map context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) { + return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op); + } + + public Contexts(Map context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) { if (context == null) { throw new NullPointerException("context"); } @@ -59,6 +63,11 @@ public class Contexts { this.applyGroups = applyGroups; this.applyGlobalGroups = applyGlobalGroups; this.applyGlobalWorldGroups = applyGlobalWorldGroups; + this.op = op; + } + + public Contexts(Map context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) { + this(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, false); } /** @@ -68,6 +77,11 @@ public class Contexts { */ private final Map context; + /** + * The mode to parse defaults on Bukkit + */ + private final boolean op; + /** * If global or non server specific nodes should be applied */ @@ -101,6 +115,14 @@ public class Contexts { return this.context; } + /** + * Gets if OP defaults should be included + * @return true if op defaults should be included + */ + public boolean isOp() { + return this.op; + } + /** * Gets if global or non server specific nodes should be applied * @return true if global or non server specific nodes should be applied @@ -144,6 +166,7 @@ public class Contexts { public String toString() { return "Contexts(" + "context=" + this.getContext() + ", " + + "op=" + this.isOp() + ", " + "includeGlobal=" + this.isIncludeGlobal() + ", " + "includeGlobalWorld=" + this.isIncludeGlobalWorld() + ", " + "applyGroups=" + this.isApplyGroups() + ", " + @@ -152,4 +175,49 @@ public class Contexts { ")"; } + /* + * Ugly auto-generated lombok code + */ + + /** + * Check for equality + * @param o the other + * @return true if equal + * @since 2.12 + */ + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof Contexts)) return false; + final Contexts other = (Contexts) o; + final Object this$context = this.getContext(); + final Object other$context = other.getContext(); + if (this$context == null ? other$context != null : !this$context.equals(other$context)) return false; + if (this.isOp() != other.isOp()) return false; + if (this.isIncludeGlobal() != other.isIncludeGlobal()) return false; + if (this.isIncludeGlobalWorld() != other.isIncludeGlobalWorld()) return false; + if (this.isApplyGroups() != other.isApplyGroups()) return false; + if (this.isApplyGlobalGroups() != other.isApplyGlobalGroups()) return false; + if (this.isApplyGlobalWorldGroups() != other.isApplyGlobalWorldGroups()) return false; + return true; + } + + /** + * Gets the hashcode + * @return the hashcode + * @since 2.12 + */ + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $context = this.getContext(); + result = result * PRIME + ($context == null ? 43 : $context.hashCode()); + result = result * PRIME + (this.isOp() ? 79 : 97); + result = result * PRIME + (this.isIncludeGlobal() ? 79 : 97); + result = result * PRIME + (this.isIncludeGlobalWorld() ? 79 : 97); + result = result * PRIME + (this.isApplyGroups() ? 79 : 97); + result = result * PRIME + (this.isApplyGlobalGroups() ? 79 : 97); + result = result * PRIME + (this.isApplyGlobalWorldGroups() ? 79 : 97); + return result; + } + } diff --git a/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/ContextCache.java b/bukkit/src/main/java/me/lucko/luckperms/BukkitCalculatorFactory.java similarity index 54% rename from bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/ContextCache.java rename to bukkit/src/main/java/me/lucko/luckperms/BukkitCalculatorFactory.java index 54cf7a492..442d19e1b 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/ContextCache.java +++ b/bukkit/src/main/java/me/lucko/luckperms/BukkitCalculatorFactory.java @@ -20,53 +20,42 @@ * SOFTWARE. */ -package me.lucko.luckperms.api.vault.cache; +package me.lucko.luckperms; -import lombok.Getter; -import lombok.NonNull; -import me.lucko.luckperms.LuckPermsPlugin; -import me.lucko.luckperms.api.Tristate; +import lombok.AllArgsConstructor; +import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.calculators.*; -import me.lucko.luckperms.users.BukkitUser; +import me.lucko.luckperms.inject.Injector; +import me.lucko.luckperms.inject.LPPermissible; import me.lucko.luckperms.users.User; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; -public class ContextCache { +@AllArgsConstructor +public class BukkitCalculatorFactory implements CalculatorFactory { + private final LPBukkitPlugin plugin; - @Getter - private final Map context; - - @Getter - private final Map permissionCache = new ConcurrentHashMap<>(); - - private final PermissionCalculator calculator; - - public ContextCache(User user, Map context, LuckPermsPlugin plugin, DefaultsProvider defaultsProvider) { - this.context = context; + @Override + public PermissionCalculator build(Contexts contexts, User user, Map map) { + UUID uuid = plugin.getUuidCache().getExternalUUID(user.getUuid()); List processors = new ArrayList<>(5); - processors.add(new MapProcessor(permissionCache)); + processors.add(new MapProcessor(map)); + processors.add(new AttachmentProcessor(() -> { + LPPermissible permissible = Injector.getPermissible(uuid); + return permissible == null ? null : permissible.getAttachmentPermissions(); + })); if (plugin.getConfiguration().isApplyingWildcards()) { - processors.add(new WildcardProcessor(permissionCache)); + processors.add(new WildcardProcessor(map)); } if (plugin.getConfiguration().isApplyingRegex()) { - processors.add(new RegexProcessor(permissionCache)); + processors.add(new RegexProcessor(map)); } + processors.add(new DefaultsProcessor(contexts.isOp(), plugin.getDefaultsProvider())); - processors.add(new DefaultsProcessor(((BukkitUser) user)::isOp, defaultsProvider)); - calculator = new PermissionCalculator(plugin, user.getName(), plugin.getConfiguration().isDebugPermissionChecks(), processors); + return new PermissionCalculator(plugin, user.getName(), plugin.getConfiguration().isDebugPermissionChecks(), processors); } - - public void invalidateCache() { - calculator.invalidateCache(); - } - - public Tristate getPermissionValue(@NonNull String permission) { - return calculator.getPermissionValue(permission); - } - } diff --git a/bukkit/src/main/java/me/lucko/luckperms/BukkitListener.java b/bukkit/src/main/java/me/lucko/luckperms/BukkitListener.java index aeb67bcdf..a2d01de8e 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/BukkitListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/BukkitListener.java @@ -22,21 +22,20 @@ package me.lucko.luckperms; -import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.inject.Injector; import me.lucko.luckperms.inject.LPPermissible; -import me.lucko.luckperms.users.BukkitUser; import me.lucko.luckperms.users.User; import me.lucko.luckperms.utils.AbstractListener; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.player.*; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; -import java.util.Collections; -import java.util.Map; import java.util.UUID; class BukkitListener extends AbstractListener implements Listener { @@ -50,6 +49,7 @@ class BukkitListener extends AbstractListener implements Listener { @EventHandler(priority = EventPriority.LOWEST) public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) { if (!plugin.getDatastore().isAcceptingLogins()) { + // The datastore is disabled, prevent players from joining the server e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.toString()); return; @@ -57,38 +57,14 @@ class BukkitListener extends AbstractListener implements Listener { // Process login onAsyncLogin(e.getUniqueId(), e.getName()); - - // Pre-process the user's permissions, so they're ready for PLE. - BukkitUser user = (BukkitUser) plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getUniqueId())); - Map toApply = user.exportNodes( - new Contexts( - Collections.singletonMap("server", plugin.getConfiguration().getServer()), - plugin.getConfiguration().isIncludingGlobalPerms(), - plugin.getConfiguration().isIncludingGlobalWorldPerms(), - true, - plugin.getConfiguration().isApplyingGlobalGroups(), - plugin.getConfiguration().isApplyingGlobalWorldGroups() - ), - Collections.emptyList(), - true - ); - user.setLoginPreProcess(toApply); - - // Hook with Vault early - if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) { - plugin.getVaultHook().getPermissionHook().getVaultUserManager().setupUser(user); - } } @EventHandler(priority = EventPriority.MONITOR) public void onPlayerPreLoginMonitor(AsyncPlayerPreLoginEvent e) { if (plugin.getDatastore().isAcceptingLogins() && e.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { + // Login event was cancelled by another plugin - final UUID internal = plugin.getUuidCache().getUUID(e.getUniqueId()); onLeave(e.getUniqueId()); - if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) { - plugin.getVaultHook().getPermissionHook().getVaultUserManager().clearUser(internal); - } } } @@ -98,61 +74,56 @@ class BukkitListener extends AbstractListener implements Listener { final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId())); if (user == null) { + // User wasn't loaded for whatever reason. e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.toString()); return; } - BukkitUser u = (BukkitUser) user; try { // Make a new permissible for the user - LPPermissible lpPermissible = new LPPermissible(player, plugin, plugin.getDefaultsProvider()); - - // Insert the pre-processed permissions into the permissible - lpPermissible.getLuckPermsPermissions().putAll(u.getLoginPreProcess()); - u.setLoginPreProcess(null); + LPPermissible lpPermissible = new LPPermissible(player, user, plugin); // Inject into the player Injector.inject(player, lpPermissible); - u.setPermissible(lpPermissible); } catch (Throwable t) { t.printStackTrace(); } + + if (player.isOp()) { + + // We assume all users are not op, but those who are need extra calculation. + plugin.doAsync(() -> user.getUserData().preCalculate(plugin.getPreProcessContexts(true))); + } } @EventHandler(priority = EventPriority.MONITOR) public void onPlayerLoginMonitor(PlayerLoginEvent e) { if (e.getResult() != PlayerLoginEvent.Result.ALLOWED) { + // The player got denied on sync login. - final UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()); onLeave(e.getPlayer().getUniqueId()); - if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) { - plugin.getVaultHook().getPermissionHook().getVaultUserManager().clearUser(internal); - } - } else { - User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId())); - - // Call another update to calculate full context. (incl. per world permissions) - plugin.doAsync(user::refreshPermissions); } } - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerJoin(PlayerJoinEvent e) { - // Refresh permissions again - UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()); - plugin.getWorldCalculator().getWorldCache().put(internal, e.getPlayer().getWorld().getName()); - plugin.doAsync(() -> refreshPlayer(internal)); - } - - @EventHandler(priority = EventPriority.LOWEST) + @EventHandler(priority = EventPriority.HIGHEST) // Allow other plugins to see data when this event gets called. public void onPlayerQuit(PlayerQuitEvent e) { - final UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()); + final Player player = e.getPlayer(); + final UUID internal = plugin.getUuidCache().getUUID(player.getUniqueId()); + + // Remove from World cache plugin.getWorldCalculator().getWorldCache().remove(internal); - onLeave(e.getPlayer().getUniqueId()); - if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) { - plugin.getVaultHook().getPermissionHook().getVaultUserManager().clearUser(internal); + + // Remove the custom permissible + Injector.unInject(player); + + // Handle auto op + if (plugin.getConfiguration().isAutoOp()) { + player.setOp(false); } + + // Call internal leave handling + onLeave(player.getUniqueId()); } @EventHandler diff --git a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java index f64fa834c..c3da67d18 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java @@ -23,11 +23,13 @@ package me.lucko.luckperms; import lombok.Getter; +import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.api.LuckPermsApi; import me.lucko.luckperms.api.PlatformType; import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.api.vault.VaultHook; +import me.lucko.luckperms.calculators.CalculatorFactory; import me.lucko.luckperms.calculators.DefaultsProvider; import me.lucko.luckperms.commands.ConsecutiveExecutor; import me.lucko.luckperms.commands.Sender; @@ -35,7 +37,6 @@ import me.lucko.luckperms.config.LPConfiguration; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.contexts.ContextManager; import me.lucko.luckperms.contexts.ServerCalculator; -import me.lucko.luckperms.contexts.WorldCalculator; import me.lucko.luckperms.core.UuidCache; import me.lucko.luckperms.data.Importer; import me.lucko.luckperms.groups.GroupManager; @@ -44,9 +45,10 @@ import me.lucko.luckperms.runnables.UpdateTask; import me.lucko.luckperms.storage.Datastore; import me.lucko.luckperms.storage.StorageFactory; import me.lucko.luckperms.tracks.TrackManager; -import me.lucko.luckperms.users.BukkitUserManager; +import me.lucko.luckperms.users.UserManager; import me.lucko.luckperms.utils.LocaleManager; import me.lucko.luckperms.utils.LogFactory; +import org.bukkit.World; import org.bukkit.command.PluginCommand; import org.bukkit.entity.Player; import org.bukkit.permissions.Permission; @@ -66,7 +68,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { private final Set ignoringLogs = ConcurrentHashMap.newKeySet(); private LPConfiguration configuration; - private BukkitUserManager userManager; + private UserManager userManager; private GroupManager groupManager; private TrackManager trackManager; private Datastore datastore; @@ -79,6 +81,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { private LocaleManager localeManager; private ContextManager contextManager; private WorldCalculator worldCalculator; + private CalculatorFactory calculatorFactory; @Override public void onEnable() { @@ -117,18 +120,18 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { getLog().info("Loading internal permission managers..."); uuidCache = new UuidCache(getConfiguration().isOnlineMode()); - userManager = new BukkitUserManager(this); + userManager = new UserManager(this); groupManager = new GroupManager(this); trackManager = new TrackManager(); importer = new Importer(commandManager); consecutiveExecutor = new ConsecutiveExecutor(commandManager); + calculatorFactory = new BukkitCalculatorFactory(this); contextManager = new ContextManager<>(); worldCalculator = new WorldCalculator(this); pm.registerEvents(worldCalculator, this); contextManager.registerCalculator(worldCalculator); contextManager.registerCalculator(new ServerCalculator<>(getConfiguration().getServer())); - contextManager.registerListener(userManager); int mins = getConfiguration().getSyncTime(); if (mins > 0) { @@ -244,15 +247,68 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { } @Override - public List getPossiblePermissions() { - final List perms = new ArrayList<>(); + public Set getPreProcessContexts(boolean op) { + Set> c = new HashSet<>(); + c.add(Collections.emptyMap()); + c.add(Collections.singletonMap("server", getConfiguration().getServer())); - getServer().getPluginManager().getPermissions().forEach(p -> { - perms.add(p.getName()); - p.getChildren().keySet().forEach(perms::add); - }); + // Pre process all worlds + c.addAll(getServer().getWorlds().stream() + .map(World::getName) + .map(s -> { + Map map = new HashMap<>(); + map.put("server", getConfiguration().getServer()); + map.put("world", s); + return map; + }) + .collect(Collectors.toList()) + ); - return perms; + // Pre process the separate Vault server, if any + if (!getConfiguration().getServer().equals(getConfiguration().getVaultServer())) { + c.add(Collections.singletonMap("server", getConfiguration().getVaultServer())); + c.addAll(getServer().getWorlds().stream() + .map(World::getName) + .map(s -> { + Map map = new HashMap<>(); + map.put("server", getConfiguration().getVaultServer()); + map.put("world", s); + return map; + }) + .collect(Collectors.toList()) + ); + } + + Set contexts = new HashSet<>(); + + // Convert to full Contexts + contexts.addAll(c.stream() + .map(map -> new Contexts( + map, + getConfiguration().isIncludingGlobalPerms(), + getConfiguration().isIncludingGlobalWorldPerms(), + true, + getConfiguration().isApplyingGlobalGroups(), + getConfiguration().isApplyingGlobalWorldGroups(), + op + )) + .collect(Collectors.toSet()) + ); + + // Check for and include varying Vault config options + try { + assert getConfiguration().isVaultIncludingGlobal() == getConfiguration().isIncludingGlobalPerms(); + assert getConfiguration().isIncludingGlobalWorldPerms(); + assert getConfiguration().isApplyingGlobalGroups(); + assert getConfiguration().isApplyingGlobalWorldGroups(); + } catch (AssertionError e) { + contexts.addAll(c.stream() + .map(map -> new Contexts(map, getConfiguration().isVaultIncludingGlobal(), true, true, true, true, op)) + .collect(Collectors.toSet()) + ); + } + + return contexts; } @Override diff --git a/bukkit/src/main/java/me/lucko/luckperms/contexts/WorldCalculator.java b/bukkit/src/main/java/me/lucko/luckperms/WorldCalculator.java similarity index 98% rename from bukkit/src/main/java/me/lucko/luckperms/contexts/WorldCalculator.java rename to bukkit/src/main/java/me/lucko/luckperms/WorldCalculator.java index 7b1a3432e..65dc4fc08 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/contexts/WorldCalculator.java +++ b/bukkit/src/main/java/me/lucko/luckperms/WorldCalculator.java @@ -20,7 +20,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.contexts; +package me.lucko.luckperms; import com.google.common.collect.Maps; import lombok.Getter; diff --git a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultChatHook.java b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultChatHook.java index fcbf59fb8..b3ff9e06b 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultChatHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultChatHook.java @@ -25,8 +25,7 @@ package me.lucko.luckperms.api.vault; import lombok.NonNull; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Node; -import me.lucko.luckperms.api.vault.cache.ChatCache; -import me.lucko.luckperms.api.vault.cache.VaultUser; +import me.lucko.luckperms.caching.MetaData; import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectLacksException; @@ -74,14 +73,14 @@ public class VaultChatHook extends Chat { return perms.isEnabled(); } - private void saveMeta(PermissionHolder holder, String world, String node, String value) { + private void setMeta(PermissionHolder holder, String world, String node, String value) { String finalWorld = perms.isIgnoreWorld() ? null : world; if (holder == null) return; if (node.equals("")) return; perms.log("Setting meta: '" + node + "' for " + holder.getObjectName() + " on world " + world + ", server " + perms.getServer()); - perms.scheduleTask(() -> { + perms.getScheduler().scheduleTask(() -> { String k = escapeCharacters(node); String v = escapeCharacters(value); @@ -118,7 +117,7 @@ public class VaultChatHook extends Chat { perms.log("Setting " + (prefix ? "prefix" : "suffix") + " for " + holder.getObjectName() + " on world " + world + ", server " + perms.getServer()); - perms.scheduleTask(() -> { + perms.getScheduler().scheduleTask(() -> { Node.Builder node = new me.lucko.luckperms.core.Node.Builder(prefix ? "prefix" : "suffix" + ".1000." + escapeCharacters(value)); node.setValue(true); if (!perms.getServer().equalsIgnoreCase("global")) { @@ -138,46 +137,32 @@ public class VaultChatHook extends Chat { } private String getUserMeta(User user, String world, String node, String defaultValue) { - world = perms.isIgnoreWorld() ? null : world; if (user == null) return defaultValue; + world = perms.isIgnoreWorld() ? null : world; node = escapeCharacters(node); perms.log("Getting meta: '" + node + "' for user " + user.getName() + " on world " + world + ", server " + perms.getServer()); - if (!perms.getVaultUserManager().containsUser(user.getUuid())) { + if (user.getUserData() == null) { return defaultValue; } - VaultUser vaultUser = perms.getVaultUserManager().getUser(user.getUuid()); - Map context = new HashMap<>(); - context.put("server", perms.getServer()); - if (world != null) { - context.put("world", world); - } - - ChatCache cd = vaultUser.processChatData(context); - return unescapeCharacters(cd.getMeta().getOrDefault(node, defaultValue)); + return unescapeCharacters(user.getUserData().getMetaData(perms.createContext(perms.getServer(), world)).getMeta().getOrDefault(node, defaultValue)); } private String getUserChatMeta(boolean prefix, User user, String world) { - world = perms.isIgnoreWorld() ? null : world; if (user == null) return ""; + world = perms.isIgnoreWorld() ? null : world; perms.log("Getting " + (prefix ? "prefix" : "suffix") + " for user " + user.getName() + " on world " + world + ", server " + perms.getServer()); - if (!perms.getVaultUserManager().containsUser(user.getUuid())) { + if (user.getUserData() == null) { return ""; } - VaultUser vaultUser = perms.getVaultUserManager().getUser(user.getUuid()); - Map context = new HashMap<>(); - context.put("server", perms.getServer()); - if (world != null) { - context.put("world", world); - } - - ChatCache cd = vaultUser.processChatData(context); - return unescapeCharacters(prefix ? (cd.getPrefix() == null ? "" : cd.getPrefix()) : (cd.getSuffix() == null ? "" : cd.getSuffix())); + MetaData data = user.getUserData().getMetaData(perms.createContext(perms.getServer(), world)); + String v = prefix ? data.getPrefix() : data.getSuffix(); + return v == null ? "" : unescapeCharacters(v); } private String getGroupMeta(Group group, String world, String node, String defaultValue) { @@ -307,7 +292,7 @@ public class VaultChatHook extends Chat { public void setPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int value) { final User user = perms.getPlugin().getUserManager().get(player); - saveMeta(user, world, node, String.valueOf(value)); + setMeta(user, world, node, String.valueOf(value)); } public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) { @@ -321,7 +306,7 @@ public class VaultChatHook extends Chat { public void setGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int value) { final Group g = perms.getPlugin().getGroupManager().get(group); - saveMeta(g, world, node, String.valueOf(value)); + setMeta(g, world, node, String.valueOf(value)); } public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) { @@ -335,7 +320,7 @@ public class VaultChatHook extends Chat { public void setPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double value) { final User user = perms.getPlugin().getUserManager().get(player); - saveMeta(user, world, node, String.valueOf(value)); + setMeta(user, world, node, String.valueOf(value)); } public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) { @@ -349,7 +334,7 @@ public class VaultChatHook extends Chat { public void setGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double value) { final Group g = perms.getPlugin().getGroupManager().get(group); - saveMeta(g, world, node, String.valueOf(value)); + setMeta(g, world, node, String.valueOf(value)); } public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) { @@ -363,7 +348,7 @@ public class VaultChatHook extends Chat { public void setPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean value) { final User user = perms.getPlugin().getUserManager().get(player); - saveMeta(user, world, node, String.valueOf(value)); + setMeta(user, world, node, String.valueOf(value)); } public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) { @@ -377,7 +362,7 @@ public class VaultChatHook extends Chat { public void setGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean value) { final Group g = perms.getPlugin().getGroupManager().get(group); - saveMeta(g, world, node, String.valueOf(value)); + setMeta(g, world, node, String.valueOf(value)); } public String getPlayerInfoString(String world, @NonNull String player, @NonNull String node, String defaultValue) { @@ -387,7 +372,7 @@ public class VaultChatHook extends Chat { public void setPlayerInfoString(String world, @NonNull String player, @NonNull String node, String value) { final User user = perms.getPlugin().getUserManager().get(player); - saveMeta(user, world, node, value); + setMeta(user, world, node, value); } public String getGroupInfoString(String world, @NonNull String group, @NonNull String node, String defaultValue) { @@ -397,7 +382,7 @@ public class VaultChatHook extends Chat { public void setGroupInfoString(String world, @NonNull String group, @NonNull String node, String value) { final Group g = perms.getPlugin().getGroupManager().get(group); - saveMeta(g, world, node, value); + setMeta(g, world, node, value); } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultPermissionHook.java b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultPermissionHook.java index beecd87b5..727eefdbc 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultPermissionHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultPermissionHook.java @@ -28,8 +28,6 @@ import lombok.Setter; import me.lucko.luckperms.LPBukkitPlugin; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Node; -import me.lucko.luckperms.api.vault.cache.VaultUser; -import me.lucko.luckperms.api.vault.cache.VaultUserManager; import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectLacksException; @@ -37,21 +35,22 @@ import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.users.User; import net.milkbowl.vault.permission.Permission; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** * The LuckPerms Vault Permission implementation * Most lookups are cached. */ -public class VaultPermissionHook extends Permission implements Runnable { - private final List tasks = new ArrayList<>(); +public class VaultPermissionHook extends Permission { @Getter @Setter private LPBukkitPlugin plugin; @Getter - private VaultUserManager vaultUserManager; + private VaultScheduler scheduler; @Getter @Setter @@ -66,8 +65,7 @@ public class VaultPermissionHook extends Permission implements Runnable { private boolean ignoreWorld = false; public void setup() { - vaultUserManager = new VaultUserManager(plugin, this); - plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this, 1L, 1L); + scheduler = new VaultScheduler(plugin); } public void log(String s) { @@ -76,23 +74,6 @@ public class VaultPermissionHook extends Permission implements Runnable { } } - void scheduleTask(Runnable r) { - synchronized (tasks) { - tasks.add(r); - } - } - - @Override - public void run() { - List toRun = new ArrayList<>(); - synchronized (tasks) { - toRun.addAll(tasks); - tasks.clear(); - } - - toRun.forEach(Runnable::run); - } - @Override public String getName() { return "LuckPerms"; @@ -108,133 +89,149 @@ public class VaultPermissionHook extends Permission implements Runnable { return true; } - private boolean objectHas(String world, Group group, String permission) { - if (group == null) return false; + /** + * Generic method to add a permission to a holder + * @param world the world to add in + * @param holder the holder to add the permission to + * @param permission the permission to add + */ + private void add(String world, PermissionHolder holder, String permission) { + try { + if (world != null && !world.equals("")) { + holder.setPermission(permission, true, server, world); + } else { + holder.setPermission(permission, true, server); + } + } catch (ObjectAlreadyHasException ignored) {} + save(holder); + } + + /** + * Generic method to remove a permission from a holder + * @param world the world to remove in + * @param holder the holder to remove the permission from + * @param permission the permission to remove + */ + private void remove(String world, PermissionHolder holder, String permission) { + try { + if (world != null && !world.equals("")) { + holder.unsetPermission(permission, server, world); + } else { + holder.unsetPermission(permission, server); + } + } catch (ObjectLacksException ignored) {} + + save(holder); + } + + /** + * Utility method for saving a user or group + * @param holder the holder instance + */ + void save(PermissionHolder holder) { + if (holder instanceof User) { + ((User) holder).refreshPermissions(); + plugin.getDatastore().saveUser(((User) holder)); + } + if (holder instanceof Group) { + plugin.getDatastore().saveGroup(((Group) holder)); + plugin.runUpdateTask(); + } + } + + Contexts createContext(String server, String world) { Map context = new HashMap<>(); if (world != null && !world.equals("")) { context.put("world", world); } context.put("server", server); - - Map toApply = group.exportNodes( - new Contexts(context, includeGlobal, includeGlobal, true, true, true), - Collections.emptyList(), true - ); - - return toApply.containsKey(permission) && toApply.get(permission); - } - - private boolean add(String world, PermissionHolder object, String permission) { - if (object == null) return false; - - try { - if (world != null && !world.equals("")) { - object.setPermission(permission, true, server, world); - } else { - object.setPermission(permission, true, server); - } - } catch (ObjectAlreadyHasException ignored) {} - - save(object); - return true; - } - - private boolean remove(String world, PermissionHolder object, String permission) { - if (object == null) return false; - - try { - if (world != null && !world.equals("")) { - object.unsetPermission(permission, server, world); - } else { - object.unsetPermission(permission, server); - } - } catch (ObjectLacksException ignored) {} - - save(object); - return true; - } - - void save(PermissionHolder t) { - if (t instanceof User) { - ((User) t).refreshPermissions(); - plugin.getDatastore().saveUser(((User) t)); - } - if (t instanceof Group) { - plugin.getDatastore().saveGroup(((Group) t)); - plugin.runUpdateTask(); - } + return new Contexts(context, isIncludeGlobal(), true, true, true, true); } @Override public boolean playerHas(String world, @NonNull String player, @NonNull String permission) { - world = ignoreWorld ? null : world; + world = ignoreWorld ? null : world; // Correct world value log("Checking if player " + player + " has permission: " + permission + " on world " + world + ", server " + server); + User user = plugin.getUserManager().get(player); if (user == null) return false; - if (!vaultUserManager.containsUser(user.getUuid())) { + if (user.getUserData() == null) { return false; } - VaultUser vaultUser = vaultUserManager.getUser(user.getUuid()); - Map context = new HashMap<>(); - context.put("server", server); - if (world != null) { - context.put("world", world); - } - - return vaultUser.hasPermission(context, permission); + // Effectively fallback to the standard Bukkit #hasPermission check. + return user.getUserData().getPermissionData(createContext(server, world)).getPermissionValue(permission).asBoolean(); } @Override public boolean playerAdd(String world, @NonNull String player, @NonNull String permission) { - String finalWorld = ignoreWorld ? null : world; + String finalWorld = ignoreWorld ? null : world; // Correct world value log("Adding permission to player " + player + ": '" + permission + "' on world " + finalWorld + ", server " + server); + final User user = plugin.getUserManager().get(player); - scheduleTask(() -> add(finalWorld, user, permission)); + if (user == null) return false; + + scheduler.scheduleTask(() -> add(finalWorld, user, permission)); return true; } @Override public boolean playerRemove(String world, @NonNull String player, @NonNull String permission) { - String finalWorld = ignoreWorld ? null : world; + String finalWorld = ignoreWorld ? null : world; // Correct world value log("Removing permission from player " + player + ": '" + permission + "' on world " + finalWorld + ", server " + server); + final User user = plugin.getUserManager().get(player); - scheduleTask(() -> remove(finalWorld, user, permission)); + if (user == null) return false; + + scheduler.scheduleTask(() -> remove(finalWorld, user, permission)); return true; } @Override public boolean groupHas(String world, @NonNull String groupName, @NonNull String permission) { - world = ignoreWorld ? null : world; + world = ignoreWorld ? null : world; // Correct world value log("Checking if group " + groupName + " has permission: " + permission + " on world " + world + ", server " + server); + final Group group = plugin.getGroupManager().get(groupName); - return objectHas(world, group, permission); + if (group == null) return false; + + // This is a nasty call. Groups aren't cached. :( + Map permissions = group.exportNodes(createContext(server, world), Collections.emptyList(), true); + + return permissions.containsKey(permission) && permissions.get(permission); } @Override public boolean groupAdd(String world, @NonNull String groupName, @NonNull String permission) { - String finalWorld = ignoreWorld ? null : world; + String finalWorld = ignoreWorld ? null : world; // Correct world value log("Adding permission to group " + groupName + ": '" + permission + "' on world " + finalWorld + ", server " + server); + final Group group = plugin.getGroupManager().get(groupName); - scheduleTask(() -> add(finalWorld, group, permission)); + if (group == null) return false; + + scheduler.scheduleTask(() -> add(finalWorld, group, permission)); return true; } @Override public boolean groupRemove(String world, @NonNull String groupName, @NonNull String permission) { - String finalWorld = ignoreWorld ? null : world; + String finalWorld = ignoreWorld ? null : world; // Correct world value log("Removing permission from group " + groupName + ": '" + permission + "' on world " + finalWorld + ", server " + server); + final Group group = plugin.getGroupManager().get(groupName); - scheduleTask(() -> remove(finalWorld, group, permission)); + if (group == null) return false; + + scheduler.scheduleTask(() -> remove(finalWorld, group, permission)); return true; } @Override public boolean playerInGroup(String world, @NonNull String player, @NonNull String group) { - String finalWorld = ignoreWorld ? null : world; + String finalWorld = ignoreWorld ? null : world; // Correct world value log("Checking if player " + player + " is in group: " + group + " on world " + finalWorld + ", server " + server); + final User user = plugin.getUserManager().get(player); if (user == null) return false; @@ -249,15 +246,16 @@ public class VaultPermissionHook extends Permission implements Runnable { @Override public boolean playerAddGroup(String world, @NonNull String player, @NonNull String groupName) { - String finalWorld = ignoreWorld ? null : world; + String finalWorld = ignoreWorld ? null : world; // Correct world value log("Adding player " + player + " to group: '" + groupName + "' on world " + finalWorld + ", server " + server); + final User user = plugin.getUserManager().get(player); if (user == null) return false; final Group group = plugin.getGroupManager().get(groupName); if (group == null) return false; - scheduleTask(() -> { + scheduler.scheduleTask(() -> { try { if (finalWorld != null && !finalWorld.equals("")) { user.addGroup(group, server, finalWorld); @@ -272,15 +270,16 @@ public class VaultPermissionHook extends Permission implements Runnable { @Override public boolean playerRemoveGroup(String world, @NonNull String player, @NonNull String groupName) { - String finalWorld = ignoreWorld ? null : world; + String finalWorld = ignoreWorld ? null : world; // Correct world value log("Removing player " + player + " from group: '" + groupName + "' on world " + finalWorld + ", server " + server); + final User user = plugin.getUserManager().get(player); if (user == null) return false; final Group group = plugin.getGroupManager().get(groupName); if (group == null) return false; - scheduleTask(() -> { + scheduler.scheduleTask(() -> { try { if (finalWorld != null && !finalWorld.equals("")) { user.removeGroup(group, server, finalWorld); @@ -295,8 +294,9 @@ public class VaultPermissionHook extends Permission implements Runnable { @Override public String[] getPlayerGroups(String world, @NonNull String player) { - String finalWorld = ignoreWorld ? null : world; + String finalWorld = ignoreWorld ? null : world; // Correct world value log("Getting groups of player: " + player + ", on world " + finalWorld + ", server " + server); + User user = plugin.getUserManager().get(player); if (user == null) return new String[0]; diff --git a/sponge/src/main/java/me/lucko/luckperms/contexts/ContextUpdateTask.java b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultScheduler.java similarity index 63% rename from sponge/src/main/java/me/lucko/luckperms/contexts/ContextUpdateTask.java rename to bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultScheduler.java index 373385eda..76e547a37 100644 --- a/sponge/src/main/java/me/lucko/luckperms/contexts/ContextUpdateTask.java +++ b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultScheduler.java @@ -20,26 +20,34 @@ * SOFTWARE. */ -package me.lucko.luckperms.contexts; +package me.lucko.luckperms.api.vault; -import lombok.AllArgsConstructor; -import me.lucko.luckperms.api.sponge.LuckPermsUserSubject; -import me.lucko.luckperms.api.sponge.collections.UserCollection; +import me.lucko.luckperms.LPBukkitPlugin; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; -@AllArgsConstructor -public class ContextUpdateTask implements Runnable { - private final UserCollection userCollection; +public class VaultScheduler implements Runnable { + private final List tasks = new ArrayList<>(); - @Override - public void run() { - for (LuckPermsUserSubject subject : userCollection.getUsers().values()) { - Set> contexts = new HashSet<>(subject.getContextData().keySet()); - contexts.forEach(map -> subject.calculatePermissions(map, true)); + public VaultScheduler(LPBukkitPlugin plugin) { + plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this, 1L, 1L); + } + + public void scheduleTask(Runnable r) { + synchronized (tasks) { + tasks.add(r); } } + @Override + public void run() { + List toRun = new ArrayList<>(); + synchronized (tasks) { + toRun.addAll(tasks); + tasks.clear(); + } + + toRun.forEach(Runnable::run); + } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/VaultUser.java b/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/VaultUser.java deleted file mode 100644 index 1c142f03a..000000000 --- a/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/VaultUser.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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.api.vault.cache; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import me.lucko.luckperms.LPBukkitPlugin; -import me.lucko.luckperms.api.Contexts; -import me.lucko.luckperms.api.Node; -import me.lucko.luckperms.api.vault.VaultPermissionHook; -import me.lucko.luckperms.users.User; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@RequiredArgsConstructor -public class VaultUser { - private final LPBukkitPlugin plugin; - private final VaultPermissionHook vault; - - @Getter - private final User user; - - @Getter - private final Map, ContextCache> contextData = new ConcurrentHashMap<>(); - - @Getter - private final Map, ChatCache> chatData = new ConcurrentHashMap<>(); - - public boolean hasPermission(Map context, String permission) { - ContextCache cd = contextData.computeIfAbsent(context, map -> calculatePermissions(map, false)); - return cd.getPermissionValue(permission).asBoolean(); - } - - public ChatCache processChatData(Map context) { - return chatData.computeIfAbsent(context, map -> calculateChat(map, false)); - } - - public ContextCache calculatePermissions(Map context, boolean apply) { - Map toApply = user.exportNodes( - new Contexts(context, vault.isIncludeGlobal(), true, true, true, true), - Collections.emptyList(), - true - ); - - ContextCache existing = contextData.get(context); - if (existing == null) { - existing = new ContextCache(user, context, plugin, plugin.getDefaultsProvider()); - if (apply) { - contextData.put(context, existing); - } - } - - boolean different = false; - if (toApply.size() != existing.getPermissionCache().size()) { - different = true; - } else { - for (Map.Entry e : existing.getPermissionCache().entrySet()) { - if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) { - continue; - } - different = true; - break; - } - } - - if (!different) return existing; - - existing.getPermissionCache().clear(); - existing.invalidateCache(); - existing.getPermissionCache().putAll(toApply); - return existing; - } - - public ChatCache calculateChat(Map context, boolean apply) { - ChatCache existing = chatData.get(context); - if (existing == null) { - existing = new ChatCache(context); - if (apply) { - chatData.put(context, existing); - } - } - - Map contexts = new HashMap<>(context); - String server = contexts.get("server"); - String world = contexts.get("world"); - contexts.remove("server"); - contexts.remove("world"); - - existing.invalidateCache(); - - // Load meta - for (Node n : user.getPermissions(true)) { - if (!n.getValue()) { - continue; - } - - if (!n.isMeta()) { - continue; - } - - if (!n.shouldApplyOnServer(server, vault.isIncludeGlobal(), false)) { - continue; - } - - if (!n.shouldApplyOnWorld(world, true, false)) { - continue; - } - - if (!n.shouldApplyWithContext(contexts, false)) { - continue; - } - - Map.Entry meta = n.getMeta(); - existing.getMeta().put(meta.getKey(), meta.getValue()); - } - - // Load prefixes & suffixes - - int prefixPriority = Integer.MIN_VALUE; - int suffixPriority = Integer.MIN_VALUE; - - for (Node n : user.getAllNodes(null, new Contexts(context, vault.isIncludeGlobal(), true, true, true, true))) { - if (!n.getValue()) { - continue; - } - - if (!n.isPrefix() && !n.isSuffix()) { - continue; - } - - if (!n.shouldApplyOnServer(server, vault.isIncludeGlobal(), false)) { - continue; - } - - if (!n.shouldApplyOnWorld(world, true, false)) { - continue; - } - - if (!n.shouldApplyWithContext(contexts, false)) { - continue; - } - - if (n.isPrefix()) { - Map.Entry value = n.getPrefix(); - if (value.getKey() > prefixPriority) { - existing.setPrefix(value.getValue()); - prefixPriority = value.getKey(); - } - } else { - Map.Entry value = n.getSuffix(); - if (value.getKey() > suffixPriority) { - existing.setSuffix(value.getValue()); - suffixPriority = value.getKey(); - } - } - } - - return existing; - } -} diff --git a/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/VaultUserManager.java b/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/VaultUserManager.java deleted file mode 100644 index b43c1b807..000000000 --- a/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/VaultUserManager.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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.api.vault.cache; - -import lombok.RequiredArgsConstructor; -import me.lucko.luckperms.LPBukkitPlugin; -import me.lucko.luckperms.api.vault.VaultPermissionHook; -import me.lucko.luckperms.users.User; -import org.bukkit.World; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -@RequiredArgsConstructor -public class VaultUserManager { - private final LPBukkitPlugin plugin; - private final VaultPermissionHook vault; - private final Map userCache = new ConcurrentHashMap<>(); - - public void setupUser(User user) { - VaultUser vaultUser = userCache.computeIfAbsent(user.getUuid(), uuid -> new VaultUser(plugin, vault, user)); - vaultUser.calculatePermissions(Collections.singletonMap("server", vault.getServer()), true); - vaultUser.calculateChat(Collections.singletonMap("server", vault.getServer()), true); - for (World world : plugin.getServer().getWorlds()) { - Map context = new HashMap<>(); - context.put("server", vault.getServer()); - context.put("world", world.getName()); - vaultUser.calculatePermissions(context, true); - vaultUser.calculateChat(context, true); - } - } - - public void clearUser(UUID uuid) { - userCache.remove(uuid); - } - - public boolean containsUser(UUID uuid) { - return userCache.containsKey(uuid); - } - - public VaultUser getUser(UUID uuid) { - return userCache.get(uuid); - } - -} diff --git a/bukkit/src/main/java/me/lucko/luckperms/calculators/AttachmentProcessor.java b/bukkit/src/main/java/me/lucko/luckperms/calculators/AttachmentProcessor.java index 093f463c6..4c96ebfbe 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/calculators/AttachmentProcessor.java +++ b/bukkit/src/main/java/me/lucko/luckperms/calculators/AttachmentProcessor.java @@ -28,19 +28,24 @@ import me.lucko.luckperms.api.Tristate; import org.bukkit.permissions.PermissionAttachmentInfo; import java.util.Map; +import java.util.function.Supplier; @AllArgsConstructor public class AttachmentProcessor implements PermissionProcessor { @Getter - private final Map map; + private final Supplier> map; @Override public Tristate hasPermission(String permission) { - if (map.containsKey(permission)) { - return Tristate.fromBoolean(map.get(permission).getValue()); + Map m = map.get(); + if (m == null) { + return Tristate.UNDEFINED; } + if (m.containsKey(permission)) { + return Tristate.fromBoolean(m.get(permission).getValue()); + } return Tristate.UNDEFINED; } diff --git a/bukkit/src/main/java/me/lucko/luckperms/calculators/DefaultsProcessor.java b/bukkit/src/main/java/me/lucko/luckperms/calculators/DefaultsProcessor.java index f6414ff73..a5fe3db97 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/calculators/DefaultsProcessor.java +++ b/bukkit/src/main/java/me/lucko/luckperms/calculators/DefaultsProcessor.java @@ -31,19 +31,19 @@ import java.util.function.Supplier; @AllArgsConstructor public class DefaultsProcessor implements PermissionProcessor { - private final Supplier isOp; + private final boolean isOp; private final DefaultsProvider defaultsProvider; @Override public Tristate hasPermission(String permission) { - Tristate t = defaultsProvider.hasDefault(permission, isOp.get()); + Tristate t = defaultsProvider.hasDefault(permission, isOp); if (t != Tristate.UNDEFINED) { return t; } Permission defPerm = Bukkit.getServer().getPluginManager().getPermission(permission); if (defPerm != null) { - return Tristate.fromBoolean(defPerm.getDefault().getValue(isOp.get())); + return Tristate.fromBoolean(defPerm.getDefault().getValue(isOp)); } else { return Tristate.UNDEFINED; } diff --git a/bukkit/src/main/java/me/lucko/luckperms/inject/Injector.java b/bukkit/src/main/java/me/lucko/luckperms/inject/Injector.java index 35ca8abc9..b3071ce05 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/inject/Injector.java +++ b/bukkit/src/main/java/me/lucko/luckperms/inject/Injector.java @@ -27,30 +27,33 @@ import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.permissions.Permissible; -import org.bukkit.permissions.PermissibleBase; import java.lang.reflect.Field; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; /** * Injects a {@link LPPermissible} into a {@link Player} */ @UtilityClass public class Injector { + private static final Map INJECTED_PERMISSIBLES = new ConcurrentHashMap<>(); private static Field HUMAN_ENTITY_FIELD; static { try { - HUMAN_ENTITY_FIELD = Class.forName(getInternalClassName("entity.CraftHumanEntity")).getDeclaredField("perm"); + HUMAN_ENTITY_FIELD = Class.forName(getVersionedClassName("entity.CraftHumanEntity")).getDeclaredField("perm"); HUMAN_ENTITY_FIELD.setAccessible(true); } catch (Exception e) { e.printStackTrace(); } } - public static boolean inject(CommandSender sender, PermissibleBase permissible) { + public static boolean inject(Player player, LPPermissible permissible) { try { - Field f = getPermField(sender); - f.set(sender, permissible); + HUMAN_ENTITY_FIELD.set(player, permissible); + INJECTED_PERMISSIBLES.put(player.getUniqueId(), permissible); return true; } catch (Exception e) { e.printStackTrace(); @@ -58,15 +61,16 @@ public class Injector { } } - public static boolean unInject(CommandSender sender) { + public static boolean unInject(Player player) { try { - Permissible permissible = getPermissible(sender); + Permissible permissible = (Permissible) HUMAN_ENTITY_FIELD.get(player); if (permissible instanceof LPPermissible) { /* The player is most likely leaving. Bukkit will attempt to call #clearPermissions, so we cannot set to null. However, there's no need to re-inject a real PermissibleBase, so we just inject a dummy instead. This saves tick time, pointlessly recalculating defaults when the instance will never be used. */ - getPermField(sender).set(sender, new DummyPermissibleBase()); + HUMAN_ENTITY_FIELD.set(player, new DummyPermissibleBase()); } + INJECTED_PERMISSIBLES.remove(player.getUniqueId()); return true; } catch (Exception e) { e.printStackTrace(); @@ -74,23 +78,11 @@ public class Injector { } } - private static Permissible getPermissible(CommandSender sender) { - try { - Field f = getPermField(sender); - return (Permissible) f.get(sender); - } catch (Exception e) { - throw new RuntimeException(e); - } + public static LPPermissible getPermissible(UUID uuid) { + return INJECTED_PERMISSIBLES.get(uuid); } - private static Field getPermField(CommandSender sender) { - if (sender instanceof Player) { - return HUMAN_ENTITY_FIELD; - } - throw new RuntimeException("Couldn't get perm field for sender " + sender.getClass().getName()); - } - - private static String getInternalClassName(String className) { + private static String getVersionedClassName(String className) { Class server = Bukkit.getServer().getClass(); if (!server.getSimpleName().equals("CraftServer")) { throw new RuntimeException("Couldn't inject into server " + server); diff --git a/bukkit/src/main/java/me/lucko/luckperms/inject/LPPermissible.java b/bukkit/src/main/java/me/lucko/luckperms/inject/LPPermissible.java index 0e8f3501a..979c50956 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/inject/LPPermissible.java +++ b/bukkit/src/main/java/me/lucko/luckperms/inject/LPPermissible.java @@ -24,61 +24,60 @@ package me.lucko.luckperms.inject; import lombok.Getter; import lombok.NonNull; -import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.LPBukkitPlugin; +import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Tristate; -import me.lucko.luckperms.calculators.*; +import me.lucko.luckperms.users.User; import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.bukkit.permissions.*; import org.bukkit.plugin.Plugin; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.stream.Collectors; /** * Modified PermissibleBase for LuckPerms */ -public class LPPermissible extends PermissibleBase { +public class LPPermissible extends PermissibleBase { // TODO autoop stuff @Getter - private final CommandSender parent; - - private final PermissionCalculator calculator; + private final User user; @Getter - private final Map luckPermsPermissions = new ConcurrentHashMap<>(); - private final List attachments = new LinkedList<>(); + private final Player parent; + + private final LPBukkitPlugin plugin; + + // Attachment stuff. + @Getter private final Map attachmentPermissions = new HashMap<>(); + private final List attachments = new LinkedList<>(); - public LPPermissible(@NonNull CommandSender sender, LuckPermsPlugin plugin, DefaultsProvider defaultsProvider) { - super(sender); - this.parent = sender; - - List processors = new ArrayList<>(5); - processors.add(new MapProcessor(luckPermsPermissions)); - processors.add(new AttachmentProcessor(attachmentPermissions)); - if (plugin.getConfiguration().isApplyingWildcards()) { - processors.add(new WildcardProcessor(luckPermsPermissions)); - } - if (plugin.getConfiguration().isApplyingRegex()) { - processors.add(new RegexProcessor(luckPermsPermissions)); - } - processors.add(new DefaultsProcessor(parent::isOp, defaultsProvider)); - - calculator = new PermissionCalculator(plugin, parent.getName(), plugin.getConfiguration().isDebugPermissionChecks(), processors); + public LPPermissible(@NonNull Player parent, User user, LPBukkitPlugin plugin) { + super(parent); + this.user = user; + this.parent = parent; + this.plugin = plugin; recalculatePermissions(); } - public void invalidateCache() { - calculator.invalidateCache(); + private Contexts calculateContexts() { + return new Contexts( + plugin.getContextManager().giveApplicableContext(parent, new HashMap<>()), + plugin.getConfiguration().isIncludingGlobalPerms(), + plugin.getConfiguration().isIncludingGlobalWorldPerms(), + true, + plugin.getConfiguration().isApplyingGlobalGroups(), + plugin.getConfiguration().isApplyingGlobalWorldGroups(), + parent.isOp() + ); } - @Override - public boolean isOp() { - return parent.isOp(); + private boolean hasData() { + return user.getUserData() != null; } @Override @@ -88,7 +87,7 @@ public class LPPermissible extends PermissibleBase { @Override public boolean isPermissionSet(@NonNull String name) { - return calculator.getPermissionValue(name) != Tristate.UNDEFINED; + return hasData() && user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(name) != Tristate.UNDEFINED; } @Override @@ -98,9 +97,11 @@ public class LPPermissible extends PermissibleBase { @Override public boolean hasPermission(@NonNull String name) { - Tristate ts = calculator.getPermissionValue(name); - if (ts != Tristate.UNDEFINED) { - return ts.asBoolean(); + if (hasData()) { + Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(name); + if (ts != Tristate.UNDEFINED) { + return ts.asBoolean(); + } } return Permission.DEFAULT_PERMISSION.getValue(isOp()); @@ -108,14 +109,32 @@ public class LPPermissible extends PermissibleBase { @Override public boolean hasPermission(@NonNull Permission perm) { - Tristate ts = calculator.getPermissionValue(perm.getName()); - if (ts != Tristate.UNDEFINED) { - return ts.asBoolean(); + if (hasData()) { + Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(perm.getName()); + if (ts != Tristate.UNDEFINED) { + return ts.asBoolean(); + } } return perm.getDefault().getValue(isOp()); } + @Override + public Set getEffectivePermissions() { + Set perms = new HashSet<>(); + perms.addAll(attachmentPermissions.values()); + + if (hasData()) { + perms.addAll( + user.getUserData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet().stream() + .map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue())) + .collect(Collectors.toList()) + ); + } + + return perms; + } + @Override public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value) { if (!plugin.isEnabled()) { @@ -202,7 +221,9 @@ public class LPPermissible extends PermissibleBase { calculateChildPermissions(attachment.getPermissions(), false, attachment); } - invalidateCache(); + if (hasData()) { + user.getUserData().invalidateCache(); + } } @Override @@ -234,18 +255,6 @@ public class LPPermissible extends PermissibleBase { } } - @Override - public Set getEffectivePermissions() { - Set perms = new HashSet<>(); - perms.addAll(attachmentPermissions.values()); - - perms.addAll(luckPermsPermissions.entrySet().stream() - .map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue())) - .collect(Collectors.toList())); - - return perms; - } - private class RemoveAttachmentRunnable implements Runnable { private PermissionAttachment attachment; diff --git a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java deleted file mode 100644 index 155770626..000000000 --- a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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.users; - -import lombok.Getter; -import lombok.Setter; -import me.lucko.luckperms.LPBukkitPlugin; -import me.lucko.luckperms.api.Contexts; -import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent; -import me.lucko.luckperms.api.implementation.internal.UserLink; -import me.lucko.luckperms.inject.LPPermissible; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permissible; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class BukkitUser extends User { - private final LPBukkitPlugin plugin; - - @Getter - @Setter - private LPPermissible permissible = null; - - @Getter - @Setter - private Map loginPreProcess = null; - - BukkitUser(UUID uuid, LPBukkitPlugin plugin) { - super(uuid, plugin); - this.plugin = plugin; - } - - BukkitUser(UUID uuid, String username, LPBukkitPlugin plugin) { - super(uuid, username, plugin); - this.plugin = plugin; - } - - public boolean isOp() { - return permissible != null && permissible.isOp(); - } - - @SuppressWarnings("unchecked") - @Override - public synchronized void refreshPermissions() { - LPPermissible permissible = getPermissible(); - if (permissible == null) { - return; - } - - // Calculate the permissions that should be applied. This is done async, who cares about how long it takes or how often it's done. - Map toApply = exportNodes( - new Contexts( - plugin.getContextManager().giveApplicableContext((Player) permissible.getParent(), new HashMap<>()), - plugin.getConfiguration().isIncludingGlobalPerms(), - plugin.getConfiguration().isIncludingGlobalWorldPerms(), - true, - plugin.getConfiguration().isApplyingGlobalGroups(), - plugin.getConfiguration().isApplyingGlobalWorldGroups() - ), - Collections.emptyList(), - true - ); - - try { - Map existing = permissible.getLuckPermsPermissions(); - - boolean different = false; - if (toApply.size() != existing.size()) { - different = true; - } else { - for (Map.Entry e : existing.entrySet()) { - if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) { - continue; - } - different = true; - break; - } - } - - if (!different) return; - - existing.clear(); - permissible.invalidateCache(); - existing.putAll(toApply); - - if (plugin.getConfiguration().isAutoOp()) { - boolean op = false; - - for (Map.Entry e : toApply.entrySet()) { - if (e.getKey().equalsIgnoreCase("luckperms.autoop") && e.getValue()) { - op = true; - break; - } - } - - final boolean finalOp = op; - if (permissible.isOp() != op) { - final Permissible parent = permissible.getParent(); - plugin.doSync(() -> parent.setOp(finalOp)); - } - } - - plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this))); - - } catch (Exception e) { - e.printStackTrace(); - } - - if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) { - plugin.getVaultHook().getPermissionHook().getVaultUserManager().setupUser(this); - } - } -} diff --git a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java deleted file mode 100644 index 42b29c49e..000000000 --- a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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.users; - -import me.lucko.luckperms.LPBukkitPlugin; -import me.lucko.luckperms.api.context.ContextListener; -import me.lucko.luckperms.inject.Injector; -import org.bukkit.entity.Player; - -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -public class BukkitUserManager extends UserManager implements ContextListener { - private final LPBukkitPlugin plugin; - - public BukkitUserManager(LPBukkitPlugin plugin) { - super(plugin); - this.plugin = plugin; - } - - @Override - public void preUnload(User user) { - if (user instanceof BukkitUser) { - BukkitUser u = (BukkitUser) user; - Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid())); - if (player != null) { - if (u.getPermissible() != null) { - Injector.unInject(player); - u.setPermissible(null); - } - - if (plugin.getConfiguration().isAutoOp()) { - player.setOp(false); - } - } - } - } - - @Override - public void cleanup(User user) { - if (plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())) == null) { - unload(user); - } - } - - @Override - public User apply(UserIdentifier id) { - BukkitUser user = id.getUsername() == null ? - new BukkitUser(id.getUuid(), plugin) : - new BukkitUser(id.getUuid(), id.getUsername(), plugin); - return user; - } - - @Override - public void updateAllUsers() { - // Sometimes called async, as we need to get the players on the Bukkit thread. - plugin.doSync(() -> { - Set players = plugin.getServer().getOnlinePlayers().stream() - .map(p -> plugin.getUuidCache().getUUID(p.getUniqueId())) - .collect(Collectors.toSet()); - plugin.doAsync(() -> players.forEach(u -> plugin.getDatastore().loadUser(u, "null"))); - }); - } - - @Override - public void onContextChange(Player subject, Map.Entry before, Map.Entry current) throws Exception { - UUID internal = plugin.getUuidCache().getUUID(subject.getUniqueId()); - - User user = get(internal); - if (user != null) { - plugin.doAsync(user::refreshPermissions); - } - } -} diff --git a/bungee/src/main/java/me/lucko/luckperms/contexts/BackendServerCalculator.java b/bungee/src/main/java/me/lucko/luckperms/BackendServerCalculator.java similarity index 98% rename from bungee/src/main/java/me/lucko/luckperms/contexts/BackendServerCalculator.java rename to bungee/src/main/java/me/lucko/luckperms/BackendServerCalculator.java index ece40d89b..b824ef6c6 100644 --- a/bungee/src/main/java/me/lucko/luckperms/contexts/BackendServerCalculator.java +++ b/bungee/src/main/java/me/lucko/luckperms/BackendServerCalculator.java @@ -20,7 +20,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.contexts; +package me.lucko.luckperms; import com.google.common.collect.Maps; import me.lucko.luckperms.api.context.ContextCalculator; diff --git a/bungee/src/main/java/me/lucko/luckperms/BungeePlayerCache.java b/bungee/src/main/java/me/lucko/luckperms/BungeeCalculatorFactory.java similarity index 64% rename from bungee/src/main/java/me/lucko/luckperms/BungeePlayerCache.java rename to bungee/src/main/java/me/lucko/luckperms/BungeeCalculatorFactory.java index d83154f9b..184ac760d 100644 --- a/bungee/src/main/java/me/lucko/luckperms/BungeePlayerCache.java +++ b/bungee/src/main/java/me/lucko/luckperms/BungeeCalculatorFactory.java @@ -22,39 +22,30 @@ package me.lucko.luckperms; -import lombok.Getter; +import lombok.AllArgsConstructor; +import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.calculators.*; +import me.lucko.luckperms.users.User; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -public class BungeePlayerCache { +@AllArgsConstructor +public class BungeeCalculatorFactory implements CalculatorFactory { + private final LPBungeePlugin plugin; - private final PermissionCalculator calculator; - - @Getter - private final Map permissions = new ConcurrentHashMap<>(); - - public BungeePlayerCache(LuckPermsPlugin plugin, String name) { + @Override + public PermissionCalculator build(Contexts contexts, User user, Map map) { List processors = new ArrayList<>(3); - processors.add(new MapProcessor(permissions)); + processors.add(new MapProcessor(map)); if (plugin.getConfiguration().isApplyingWildcards()) { - processors.add(new WildcardProcessor(permissions)); + processors.add(new WildcardProcessor(map)); } if (plugin.getConfiguration().isApplyingRegex()) { - processors.add(new RegexProcessor(permissions)); + processors.add(new RegexProcessor(map)); } - calculator = new PermissionCalculator(plugin, name, plugin.getConfiguration().isDebugPermissionChecks(), processors); - } - - public void invalidateCache() { - calculator.invalidateCache(); - } - - public boolean getPermissionValue(String permission) { - return calculator.getPermissionValue(permission).asBoolean(); + return new PermissionCalculator(plugin, user.getName(), plugin.getConfiguration().isDebugPermissionChecks(), processors); } } diff --git a/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java b/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java index dbf1548ee..410481daf 100644 --- a/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java +++ b/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java @@ -22,6 +22,8 @@ package me.lucko.luckperms; +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.api.event.events.UserFirstLoginEvent; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.core.UuidCache; import me.lucko.luckperms.users.User; @@ -36,6 +38,7 @@ import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; +import java.util.HashMap; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -58,12 +61,26 @@ public class BungeeListener extends AbstractListener implements Listener { final ProxiedPlayer player = ((ProxiedPlayer) e.getSender()); - BungeePlayerCache playerCache = plugin.getPlayerCache().get(plugin.getUuidCache().getUUID(player.getUniqueId())); - if (playerCache == null) { + User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId())); + if (user == null) { return; } - e.setHasPermission(playerCache.getPermissionValue(e.getPermission())); + if (user.getUserData() == null) { + plugin.getLog().warn("Player " + player.getName() + " does not have any user data setup."); + return; + } + + Contexts contexts = new Contexts( + plugin.getContextManager().giveApplicableContext(player, new HashMap<>()), + plugin.getConfiguration().isIncludingGlobalPerms(), + plugin.getConfiguration().isIncludingGlobalWorldPerms(), + true, + plugin.getConfiguration().isApplyingGlobalGroups(), + plugin.getConfiguration().isApplyingGlobalWorldGroups() + ); + + e.setHasPermission(user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission()).asBoolean()); } @EventHandler @@ -83,10 +100,16 @@ public class BungeeListener extends AbstractListener implements Listener { cache.addToCache(c.getUniqueId(), uuid); } else { // No previous data for this player + plugin.getApiProvider().fireEventAsync(new UserFirstLoginEvent(c.getUniqueId(), c.getName())); cache.addToCache(c.getUniqueId(), c.getUniqueId()); plugin.getDatastore().saveUUIDData(c.getName(), c.getUniqueId()); } } else { + UUID uuid = plugin.getDatastore().getUUID(c.getName()); + if (uuid == null) { + plugin.getApiProvider().fireEventAsync(new UserFirstLoginEvent(c.getUniqueId(), c.getName())); + } + // Online mode, no cache needed. This is just for name -> uuid lookup. plugin.getDatastore().saveUUIDData(c.getName(), c.getUniqueId()); } @@ -94,6 +117,13 @@ public class BungeeListener extends AbstractListener implements Listener { // We have to make a new user on this thread whilst the connection is being held, or we get concurrency issues as the Bukkit server // and the BungeeCord server try to make a new user at the same time. plugin.getDatastore().loadUser(cache.getUUID(c.getUniqueId()), c.getName()); + User user = plugin.getUserManager().get(cache.getUUID(c.getUniqueId())); + if (user == null) { + plugin.getLog().warn("Failed to load user: " + c.getName()); + } else { + user.setupData(false); // Pretty nasty calculation call. Sets up the caching system so data is ready when the user joins. + } + final long time = System.currentTimeMillis() - startTime; if (time >= 1000) { plugin.getLog().warn("Processing login for " + c.getName() + " took " + time + "ms."); @@ -105,20 +135,15 @@ public class BungeeListener extends AbstractListener implements Listener { @EventHandler public void onPlayerPostLogin(PostLoginEvent e) { final ProxiedPlayer player = e.getPlayer(); - final UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()); - final User user = plugin.getUserManager().get(internal); + final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId())); if (user == null) { plugin.getProxy().getScheduler().schedule(plugin, () -> player.sendMessage(WARN_MESSAGE), 3, TimeUnit.SECONDS); - } else { - plugin.getPlayerCache().put(internal, new BungeePlayerCache(plugin, e.getPlayer().getName())); - user.refreshPermissions(); } } @EventHandler public void onPlayerQuit(PlayerDisconnectEvent e) { - plugin.getPlayerCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId())); onLeave(e.getPlayer().getUniqueId()); } } diff --git a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java index f596c9779..d235b2362 100644 --- a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java @@ -23,16 +23,17 @@ package me.lucko.luckperms; import lombok.Getter; +import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.api.PlatformType; import me.lucko.luckperms.api.implementation.ApiProvider; +import me.lucko.luckperms.calculators.CalculatorFactory; import me.lucko.luckperms.commands.CommandManager; import me.lucko.luckperms.commands.ConsecutiveExecutor; import me.lucko.luckperms.commands.Sender; import me.lucko.luckperms.config.LPConfiguration; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Permission; -import me.lucko.luckperms.contexts.BackendServerCalculator; import me.lucko.luckperms.contexts.ContextManager; import me.lucko.luckperms.contexts.ServerCalculator; import me.lucko.luckperms.core.UuidCache; @@ -43,9 +44,10 @@ import me.lucko.luckperms.runnables.UpdateTask; import me.lucko.luckperms.storage.Datastore; import me.lucko.luckperms.storage.StorageFactory; import me.lucko.luckperms.tracks.TrackManager; -import me.lucko.luckperms.users.BungeeUserManager; +import me.lucko.luckperms.users.UserManager; import me.lucko.luckperms.utils.LocaleManager; import me.lucko.luckperms.utils.LogFactory; +import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Plugin; @@ -57,10 +59,9 @@ import java.util.stream.Collectors; @Getter public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { - private final Map playerCache = new ConcurrentHashMap<>(); private final Set ignoringLogs = ConcurrentHashMap.newKeySet(); private LPConfiguration configuration; - private BungeeUserManager userManager; + private UserManager userManager; private GroupManager groupManager; private TrackManager trackManager; private Datastore datastore; @@ -71,6 +72,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { private ConsecutiveExecutor consecutiveExecutor; private LocaleManager localeManager; private ContextManager contextManager; + private CalculatorFactory calculatorFactory; @Override public void onEnable() { @@ -105,18 +107,18 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { getLog().info("Loading internal permission managers..."); uuidCache = new UuidCache(getConfiguration().isOnlineMode()); - userManager = new BungeeUserManager(this); + userManager = new UserManager(this); groupManager = new GroupManager(this); trackManager = new TrackManager(); importer = new Importer(commandManager); consecutiveExecutor = new ConsecutiveExecutor(commandManager); + calculatorFactory = new BungeeCalculatorFactory(this); contextManager = new ContextManager<>(); BackendServerCalculator serverCalculator = new BackendServerCalculator(); getProxy().getPluginManager().registerListener(this, serverCalculator); contextManager.registerCalculator(serverCalculator); contextManager.registerCalculator(new ServerCalculator<>(getConfiguration().getServer())); - contextManager.registerListener(userManager); int mins = getConfiguration().getSyncTime(); if (mins > 0) { @@ -196,9 +198,31 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { } @Override - public List getPossiblePermissions() { - // No such thing on Bungee. Wildcards are processed in the listener instead. - return Collections.emptyList(); + public Set getPreProcessContexts(boolean op) { + Set> c = new HashSet<>(); + c.add(Collections.emptyMap()); + c.add(Collections.singletonMap("server", getConfiguration().getServer())); + c.addAll(getProxy().getServers().values().stream() + .map(ServerInfo::getName) + .map(s -> { + Map map = new HashMap<>(); + map.put("server", getConfiguration().getServer()); + map.put("world", s); + return map; + }) + .collect(Collectors.toList()) + ); + + return c.stream() + .map(map -> new Contexts( + map, + getConfiguration().isIncludingGlobalPerms(), + getConfiguration().isIncludingGlobalWorldPerms(), + true, + getConfiguration().isApplyingGlobalGroups(), + getConfiguration().isApplyingGlobalWorldGroups() + )) + .collect(Collectors.toSet()); } @Override diff --git a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java deleted file mode 100644 index 18bb84eac..000000000 --- a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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.users; - -import me.lucko.luckperms.BungeePlayerCache; -import me.lucko.luckperms.LPBungeePlugin; -import me.lucko.luckperms.api.Contexts; -import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent; -import me.lucko.luckperms.api.implementation.internal.UserLink; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class BungeeUser extends User { - private final LPBungeePlugin plugin; - - BungeeUser(UUID uuid, LPBungeePlugin plugin) { - super(uuid, plugin); - this.plugin = plugin; - } - - BungeeUser(UUID uuid, String username, LPBungeePlugin plugin) { - super(uuid, username, plugin); - this.plugin = plugin; - } - - @Override - public synchronized void refreshPermissions() { - ProxiedPlayer player = plugin.getProxy().getPlayer(plugin.getUuidCache().getExternalUUID(getUuid())); - if (player == null) { - return; - } - - BungeePlayerCache playerCache = plugin.getPlayerCache().get(getUuid()); - if (playerCache == null) { - return; - } - - // Calculate the permissions that should be applied. This is done async. - Map toApply = exportNodes( - new Contexts( - plugin.getContextManager().giveApplicableContext(player, new HashMap<>()), - plugin.getConfiguration().isIncludingGlobalPerms(), - plugin.getConfiguration().isIncludingGlobalWorldPerms(), - true, - plugin.getConfiguration().isApplyingGlobalGroups(), - plugin.getConfiguration().isApplyingGlobalWorldGroups() - ), - Collections.emptyList(), - true - ); - - Map existing = playerCache.getPermissions(); - - boolean different = false; - if (toApply.size() != existing.size()) { - different = true; - } else { - for (Map.Entry e : existing.entrySet()) { - if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) { - continue; - } - different = true; - break; - } - } - - if (!different) return; - - existing.clear(); - playerCache.invalidateCache(); - existing.putAll(toApply); - - plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this))); - } -} diff --git a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java deleted file mode 100644 index 344944075..000000000 --- a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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.users; - -import me.lucko.luckperms.LPBungeePlugin; -import me.lucko.luckperms.api.context.ContextListener; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -import java.util.Map; -import java.util.UUID; - -public class BungeeUserManager extends UserManager implements ContextListener { - private final LPBungeePlugin plugin; - - public BungeeUserManager(LPBungeePlugin plugin) { - super(plugin); - this.plugin = plugin; - } - - @Override - public void cleanup(User user) { - if (plugin.getProxy().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())) == null) { - unload(user); - } - } - - @Override - public User apply(UserIdentifier id) { - BungeeUser user = id.getUsername() == null ? - new BungeeUser(id.getUuid(), plugin) : - new BungeeUser(id.getUuid(), id.getUsername(), plugin); - return user; - } - - @Override - public void updateAllUsers() { - plugin.getProxy().getPlayers().stream() - .map(p -> plugin.getUuidCache().getUUID(p.getUniqueId())) - .forEach(u -> plugin.getDatastore().loadUser(u, "null")); - } - - @Override - public void onContextChange(ProxiedPlayer subject, Map.Entry before, Map.Entry current) throws Exception { - UUID internal = plugin.getUuidCache().getUUID(subject.getUniqueId()); - - User user = get(internal); - if (user != null) { - plugin.doAsync(user::refreshPermissions); - } - } -} diff --git a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java index 349183d69..21e493109 100644 --- a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java @@ -22,9 +22,11 @@ package me.lucko.luckperms; +import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.api.PlatformType; import me.lucko.luckperms.api.implementation.ApiProvider; +import me.lucko.luckperms.calculators.CalculatorFactory; import me.lucko.luckperms.commands.ConsecutiveExecutor; import me.lucko.luckperms.commands.Sender; import me.lucko.luckperms.config.LPConfiguration; @@ -64,6 +66,7 @@ public interface LuckPermsPlugin { ConsecutiveExecutor getConsecutiveExecutor(); LocaleManager getLocaleManager(); ContextManager getContextManager(); + CalculatorFactory getCalculatorFactory(); /** * @return the version of the plugin @@ -114,11 +117,8 @@ public interface LuckPermsPlugin { */ Sender getConsoleSender(); - /** - * Gets all possible permission nodes, used for resolving wildcards - * @return a {@link List} of permission nodes - */ - List getPossiblePermissions(); + // TODO javadoc + Set getPreProcessContexts(boolean op); /** * Gets a set of players ignoring logging output diff --git a/common/src/main/java/me/lucko/luckperms/caching/MetaData.java b/common/src/main/java/me/lucko/luckperms/caching/MetaData.java new file mode 100644 index 000000000..794447f2f --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/caching/MetaData.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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.caching; + +import com.google.common.collect.ImmutableMap; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.api.LocalizedNode; +import me.lucko.luckperms.api.Node; + +import java.util.HashMap; +import java.util.Map; +import java.util.SortedSet; +import java.util.concurrent.ConcurrentHashMap; + +@RequiredArgsConstructor +public class MetaData { + private final Contexts contexts; + + @Getter + private String prefix = null; + + @Getter + private String suffix = null; + private Map meta = new ConcurrentHashMap<>(); + + public void loadMeta(SortedSet nodes) { + invalidateCache(); + + Map contexts = new HashMap<>(this.contexts.getContext()); + String server = contexts.remove("server"); + String world = contexts.remove("world"); + + int prefixPriority = Integer.MIN_VALUE; + int suffixPriority = Integer.MIN_VALUE; + + for (LocalizedNode ln : nodes) { + Node n = ln.getNode(); + + if (!n.getValue()) { + continue; + } + + if (!n.isMeta() || !n.isPrefix() || n.isSuffix()) { + continue; + } + + if (!n.shouldApplyOnServer(server, this.contexts.isIncludeGlobal(), false)) { + continue; + } + + if (!n.shouldApplyOnWorld(world, this.contexts.isIncludeGlobalWorld(), false)) { + continue; + } + + if (!n.shouldApplyWithContext(contexts, false)) { + continue; + } + + if (n.isPrefix()) { + Map.Entry value = n.getPrefix(); + if (value.getKey() > prefixPriority) { + this.prefix = value.getValue(); + prefixPriority = value.getKey(); + } + continue; + } + + if (n.isSuffix()) { + Map.Entry value = n.getSuffix(); + if (value.getKey() > suffixPriority) { + this.suffix = value.getValue(); + suffixPriority = value.getKey(); + } + continue; + } + + if (n.isMeta()) { + Map.Entry meta = n.getMeta(); + this.meta.put(meta.getKey(), meta.getValue()); + } + } + } + + public void invalidateCache() { + meta.clear(); + prefix = null; + suffix = null; + } + + public Map getMeta() { + return ImmutableMap.copyOf(meta); + } + +} diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/ContextData.java b/common/src/main/java/me/lucko/luckperms/caching/PermissionData.java similarity index 50% rename from sponge/src/main/java/me/lucko/luckperms/api/sponge/ContextData.java rename to common/src/main/java/me/lucko/luckperms/caching/PermissionData.java index 2f6ded9ed..4d0ef6c04 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/ContextData.java +++ b/common/src/main/java/me/lucko/luckperms/caching/PermissionData.java @@ -20,56 +20,67 @@ * SOFTWARE. */ -package me.lucko.luckperms.api.sponge; +package me.lucko.luckperms.caching; -import lombok.Getter; +import com.google.common.collect.ImmutableMap; import lombok.NonNull; +import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Tristate; -import me.lucko.luckperms.calculators.*; -import org.spongepowered.api.service.context.Context; +import me.lucko.luckperms.calculators.CalculatorFactory; +import me.lucko.luckperms.calculators.PermissionCalculator; +import me.lucko.luckperms.users.User; -import java.util.ArrayList; -import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; -public class ContextData { - - @Getter - private final Map context; - - @Getter - private final Map permissionCache = new ConcurrentHashMap<>(); +public class PermissionData { + private final Contexts contexts; + private final Map permissions; private final PermissionCalculator calculator; - public ContextData(LuckPermsUserSubject parent, Map context, LuckPermsService service) { - this.context = context; - - Set contexts = context.entrySet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet()); - List processors = new ArrayList<>(5); - processors.add(new MapProcessor(permissionCache)); - if (service.getPlugin().getConfiguration().isApplyingWildcards()) { - processors.add(new SpongeWildcardProcessor(permissionCache)); - processors.add(new WildcardProcessor(permissionCache)); - } - if (service.getPlugin().getConfiguration().isApplyingRegex()) { - processors.add(new RegexProcessor(permissionCache)); - } - processors.add(new DefaultsProcessor(service, contexts)); - - calculator = new PermissionCalculator(service.getPlugin(), parent.getUser().getName(), service.getPlugin().getConfiguration().isDebugPermissionChecks(), processors); + public PermissionData(Contexts contexts, User user, CalculatorFactory calculatorFactory) { + this.contexts = contexts; + permissions = new ConcurrentHashMap<>(); + calculator = calculatorFactory.build(contexts, user, permissions); } public void invalidateCache() { calculator.invalidateCache(); } + public void setPermissions(Map permissions) { + this.permissions.clear(); + this.permissions.putAll(permissions); + invalidateCache(); + } + + public void comparePermissions(Map toApply) { + boolean different = false; + if (toApply.size() != permissions.size()) { + different = true; + } else { + for (Map.Entry e : permissions.entrySet()) { + if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) { + continue; + } + different = true; + break; + } + } + + if (different) { + setPermissions(toApply); + } + } + + public Map getImmutableBacking() { + return ImmutableMap.copyOf(permissions); + } + public Tristate getPermissionValue(@NonNull String permission) { - me.lucko.luckperms.api.Tristate t = calculator.getPermissionValue(permission); - if (t != me.lucko.luckperms.api.Tristate.UNDEFINED) { + Tristate t = calculator.getPermissionValue(permission); + if (t != Tristate.UNDEFINED) { return Tristate.fromBoolean(t.asBoolean()); } else { return Tristate.UNDEFINED; diff --git a/common/src/main/java/me/lucko/luckperms/caching/UserData.java b/common/src/main/java/me/lucko/luckperms/caching/UserData.java new file mode 100644 index 000000000..6e89d6520 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/caching/UserData.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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.caching; + +import lombok.RequiredArgsConstructor; +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.calculators.CalculatorFactory; +import me.lucko.luckperms.users.User; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +@RequiredArgsConstructor +public class UserData { + private final User user; + private final CalculatorFactory calculatorFactory; + + private final Map permission = new ConcurrentHashMap<>(); + private final Map meta = new ConcurrentHashMap<>(); + + public PermissionData getPermissionData(Contexts contexts) { + return permission.computeIfAbsent(contexts, this::calculatePermissions); + } + + public MetaData getMetaData(Contexts contexts) { + return meta.computeIfAbsent(contexts, this::calculateMeta); + } + + public PermissionData calculatePermissions(Contexts contexts) { + PermissionData data = new PermissionData(contexts, user, calculatorFactory); + data.setPermissions(user.exportNodes(contexts, Collections.emptyList(), true)); + return data; + } + + public void recalculatePermissions(Contexts contexts) { + permission.compute(contexts, (c, data) -> { + if (data == null) { + data = new PermissionData(c, user, calculatorFactory); + } + + data.comparePermissions(user.exportNodes(c, Collections.emptyList(), true)); + return data; + }); + } + + public void recalculatePermissions() { + permission.keySet().forEach(this::recalculatePermissions); + } + + public MetaData calculateMeta(Contexts contexts) { + MetaData data = new MetaData(contexts); + data.loadMeta(user.getAllNodes(null, contexts)); + return data; + } + + public void recalculateMeta(Contexts contexts) { + meta.compute(contexts, (c, data) -> { + if (data == null) { + data = new MetaData(c); + } + + data.loadMeta(user.getAllNodes(null, c)); + return data; + }); + } + + public void recalculateMeta() { + meta.keySet().forEach(this::recalculateMeta); + } + + public void preCalculate(Set contexts) { + contexts.forEach(this::preCalculate); + } + + public void preCalculate(Contexts contexts) { + getPermissionData(contexts); + getMetaData(contexts); + } + + public void invalidateCache() { + permission.clear(); + meta.clear(); + } + +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/ChatCache.java b/common/src/main/java/me/lucko/luckperms/calculators/CalculatorFactory.java similarity index 68% rename from bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/ChatCache.java rename to common/src/main/java/me/lucko/luckperms/calculators/CalculatorFactory.java index 031bda09c..b6fb94c57 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/api/vault/cache/ChatCache.java +++ b/common/src/main/java/me/lucko/luckperms/calculators/CalculatorFactory.java @@ -20,32 +20,18 @@ * SOFTWARE. */ -package me.lucko.luckperms.api.vault.cache; +package me.lucko.luckperms.calculators; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.users.User; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -@Getter -@RequiredArgsConstructor -public class ChatCache { - private final Map context; +/** + * Creates a calculator instance given a set of contexts + */ +public interface CalculatorFactory { - @Setter - private String prefix = null; - - @Setter - private String suffix = null; - - private Map meta = new ConcurrentHashMap<>(); - - public void invalidateCache() { - prefix = null; - suffix = null; - meta.clear(); - } + PermissionCalculator build(Contexts contexts, User user, Map map); } diff --git a/common/src/main/java/me/lucko/luckperms/users/User.java b/common/src/main/java/me/lucko/luckperms/users/User.java index 7545dfbeb..5f58ec36c 100644 --- a/common/src/main/java/me/lucko/luckperms/users/User.java +++ b/common/src/main/java/me/lucko/luckperms/users/User.java @@ -30,6 +30,7 @@ import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.api.event.events.GroupAddEvent; import me.lucko.luckperms.api.implementation.internal.GroupLink; import me.lucko.luckperms.api.implementation.internal.PermissionHolderLink; +import me.lucko.luckperms.caching.UserData; import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectLacksException; @@ -40,7 +41,7 @@ import java.util.UUID; @ToString(of = {"uuid"}) @EqualsAndHashCode(of = {"uuid"}, callSuper = false) -public abstract class User extends PermissionHolder implements Identifiable { +public class User extends PermissionHolder implements Identifiable { /** * The users Mojang UUID @@ -62,6 +63,9 @@ public abstract class User extends PermissionHolder implements Identifiable { +public class UserManager extends AbstractManager { private final LuckPermsPlugin plugin; /** @@ -121,10 +121,21 @@ public abstract class UserManager extends AbstractManager * Checks to see if the user is online, and if they are not, runs {@link #unload(Identifiable)} * @param user The user to be cleaned up */ - public abstract void cleanup(User user); + public void cleanup(User user) { + // TODO + } /** * Reloads the data of all online users */ - public abstract void updateAllUsers(); + public void updateAllUsers() { + // TODO + } + + @Override + public User apply(UserIdentifier id) { + return id.getUsername() == null ? + new User(id.getUuid(), plugin) : + new User(id.getUuid(), id.getUsername(), plugin); + } } diff --git a/common/src/main/java/me/lucko/luckperms/utils/AbstractListener.java b/common/src/main/java/me/lucko/luckperms/utils/AbstractListener.java index d9cafa41c..a8b0fbec6 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/AbstractListener.java +++ b/common/src/main/java/me/lucko/luckperms/utils/AbstractListener.java @@ -60,6 +60,13 @@ public class AbstractListener { } plugin.getDatastore().loadUser(cache.getUUID(u), username); + User user = plugin.getUserManager().get(cache.getUUID(u)); + if (user == null) { + plugin.getLog().warn("Failed to load user: " + username); + } else { + user.setupData(false); // Pretty nasty calculation call. Sets up the caching system so data is ready when the user joins. + } + final long time = System.currentTimeMillis() - startTime; if (time >= 1000) { plugin.getLog().warn("Processing login for " + username + " took " + time + "ms."); @@ -71,6 +78,7 @@ public class AbstractListener { final User user = plugin.getUserManager().get(cache.getUUID(uuid)); if (user != null) { + user.unregisterData(); plugin.getUserManager().unload(user); } diff --git a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java index bf7e4d904..2e1ee8816 100644 --- a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java @@ -34,7 +34,6 @@ import me.lucko.luckperms.config.LPConfiguration; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Permission; import me.lucko.luckperms.contexts.ContextManager; -import me.lucko.luckperms.contexts.ContextUpdateTask; import me.lucko.luckperms.contexts.ServerCalculator; import me.lucko.luckperms.contexts.WorldCalculator; import me.lucko.luckperms.core.UuidCache; @@ -46,6 +45,7 @@ import me.lucko.luckperms.storage.Datastore; import me.lucko.luckperms.storage.StorageFactory; import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.SpongeUserManager; +import me.lucko.luckperms.users.UserManager; import me.lucko.luckperms.utils.LocaleManager; import me.lucko.luckperms.utils.LogFactory; import org.slf4j.Logger; @@ -89,7 +89,7 @@ public class LPSpongePlugin implements LuckPermsPlugin { private final Set ignoringLogs = ConcurrentHashMap.newKeySet(); private LPConfiguration configuration; - private SpongeUserManager userManager; + private UserManager userManager; private GroupManager groupManager; private TrackManager trackManager; private Datastore datastore; @@ -133,7 +133,7 @@ public class LPSpongePlugin implements LuckPermsPlugin { getLog().info("Loading internal permission managers..."); uuidCache = new UuidCache(getConfiguration().isOnlineMode()); - userManager = new SpongeUserManager(this); + userManager = new UserManager(this); groupManager = new GroupManager(this); trackManager = new TrackManager(); importer = new Importer(commandManager); @@ -142,7 +142,6 @@ public class LPSpongePlugin implements LuckPermsPlugin { contextManager = new ContextManager<>(); contextManager.registerCalculator(new ServerCalculator<>(getConfiguration().getServer())); contextManager.registerCalculator(new WorldCalculator()); - contextManager.registerListener(userManager); getLog().info("Registering PermissionService..."); Sponge.getServiceManager().setProvider(this, PermissionService.class, (service = new LuckPermsService(this))); @@ -164,7 +163,6 @@ public class LPSpongePlugin implements LuckPermsPlugin { scheduler.createTaskBuilder().intervalTicks(1L).execute(SpongeSenderFactory.get(this)).submit(this); scheduler.createTaskBuilder().async().intervalTicks(60L).execute(new ExpireTemporaryTask(this)).submit(this); scheduler.createTaskBuilder().async().intervalTicks(20L).execute(consecutiveExecutor).submit(this); - scheduler.createTaskBuilder().async().intervalTicks(600L).execute(new ContextUpdateTask(service.getUserSubjects())).submit(this); getLog().info("Successfully loaded."); } @@ -252,15 +250,6 @@ public class LPSpongePlugin implements LuckPermsPlugin { return SpongeSenderFactory.get(this).wrap(game.getServer().getConsole()); } - @Override - public List getPossiblePermissions() { - Optional p = game.getServiceManager().provide(PermissionService.class); - if (!p.isPresent()) { - return Collections.emptyList(); - } - return p.get().getDescriptions().stream().map(PermissionDescription::getId).collect(Collectors.toList()); - } - @Override public Object getPlugin(String name) { return game.getPluginManager().getPlugin(name).get().getInstance().get(); diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsGroupSubject.java similarity index 64% rename from sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java rename to sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsGroupSubject.java index 957ec5996..0c8d8381d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsGroupSubject.java @@ -30,7 +30,6 @@ import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Node; import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.groups.Group; -import me.lucko.luckperms.users.User; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.permission.Subject; @@ -43,48 +42,33 @@ import java.util.stream.Collectors; import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters; -@EqualsAndHashCode(of = {"holder"}) -public class LuckPermsSubject implements Subject { - public static LuckPermsSubject wrapHolder(PermissionHolder holder, LuckPermsService service) { - return new LuckPermsSubject(holder, service); +@EqualsAndHashCode(of = "group") +public class LuckPermsGroupSubject implements Subject { + public static LuckPermsGroupSubject wrapGroup(Group group, LuckPermsService service) { + return new LuckPermsGroupSubject(group, service); } @Getter - private PermissionHolder holder; - private LuckPermsSubjectData enduringData; - private LuckPermsSubjectData transientData; - protected LuckPermsService service; + private PermissionHolder group; - LuckPermsSubject(PermissionHolder holder, LuckPermsService service) { - this.holder = holder; - this.enduringData = new LuckPermsSubjectData(true, this, service, holder); - this.transientData = new LuckPermsSubjectData(true, this, service, holder); + private LuckPermsService service; + + @Getter + private LuckPermsSubjectData subjectData; + + @Getter + private LuckPermsSubjectData transientSubjectData; + + private LuckPermsGroupSubject(PermissionHolder group, LuckPermsService service) { + this.group = group; + this.subjectData = new LuckPermsSubjectData(true, service, group); + this.transientSubjectData = new LuckPermsSubjectData(false, service, group); this.service = service; } - public void deprovision() { - holder = null; - enduringData = null; - transientData = null; - service = null; - } - - void objectSave(PermissionHolder t) { - service.getPlugin().doAsync(() -> { - if (t instanceof User) { - ((User) t).refreshPermissions(); - service.getPlugin().getDatastore().saveUser(((User) t)); - } - if (t instanceof Group) { - service.getPlugin().getDatastore().saveGroup(((Group) t)); - service.getPlugin().runUpdateTask(); - } - }); - } - @Override public String getIdentifier() { - return holder.getObjectName(); + return group.getObjectName(); } @Override @@ -94,25 +78,7 @@ public class LuckPermsSubject implements Subject { @Override public SubjectCollection getContainingCollection() { - if (holder instanceof Group) { - return service.getGroupSubjects(); - } else { - return service.getUserSubjects(); - } - } - - @Override - public SubjectData getSubjectData() { - return enduringData; - } - - @Override - public SubjectData getTransientSubjectData() { - return transientData; - } - - public boolean isPermissionSet(@NonNull Set contexts, @NonNull String node) { - return getPermissionValue(contexts, node) != Tristate.UNDEFINED; + return service.getGroupSubjects(); } @Override @@ -120,11 +86,6 @@ public class LuckPermsSubject implements Subject { return getPermissionValue(contexts, node).asBoolean(); } - @Override - public boolean hasPermission(String permission) { - return getPermissionValue(getActiveContexts(), permission).asBoolean(); - } - @Override public Tristate getPermissionValue(@NonNull Set contexts, @NonNull String node) { Map context = new HashMap<>(); @@ -132,7 +93,7 @@ public class LuckPermsSubject implements Subject { context.put(c.getKey(), c.getValue()); } - switch (holder.inheritsPermission(new me.lucko.luckperms.core.Node.Builder(node).withExtraContext(context).build())) { + switch (group.inheritsPermission(new me.lucko.luckperms.core.Node.Builder(node).withExtraContext(context).build())) { case UNDEFINED: return Tristate.UNDEFINED; case TRUE: @@ -142,53 +103,45 @@ public class LuckPermsSubject implements Subject { default: return null; } - } - @Override - public boolean isChildOf(@NonNull Subject parent) { - return isChildOf(getActiveContexts(), parent); + // TODO } @Override public boolean isChildOf(@NonNull Set contexts, @NonNull Subject parent) { - return parent instanceof PermissionHolder && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean(); - } - - @Override - public List getParents() { - return getParents(getActiveContexts()); + return parent instanceof LuckPermsGroupSubject && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean(); } @Override public List getParents(@NonNull Set contexts) { List parents = new ArrayList<>(); - parents.addAll(enduringData.getParents(contexts)); - parents.addAll(transientData.getParents(contexts)); + parents.addAll(subjectData.getParents(contexts)); + parents.addAll(transientSubjectData.getParents(contexts)); return ImmutableList.copyOf(parents); } @Override public Optional getOption(Set set, String s) { if (s.equalsIgnoreCase("prefix")) { - String prefix = getChatMeta(set, true, holder); + String prefix = getChatMeta(set, true, group); if (!prefix.equals("")) { return Optional.of(prefix); } } if (s.equalsIgnoreCase("suffix")) { - String suffix = getChatMeta(set, false, holder); + String suffix = getChatMeta(set, false, group); if (!suffix.equals("")) { return Optional.of(suffix); } } - Map transientOptions = enduringData.getOptions(set); + Map transientOptions = subjectData.getOptions(set); if (transientOptions.containsKey(s)) { return Optional.of(transientOptions.get(s)); } - Map enduringOptions = enduringData.getOptions(set); + Map enduringOptions = subjectData.getOptions(set); if (enduringOptions.containsKey(s)) { return Optional.of(enduringOptions.get(s)); } @@ -196,11 +149,6 @@ public class LuckPermsSubject implements Subject { return Optional.empty(); } - @Override - public Optional getOption(String key) { - return getOption(getActiveContexts(), key); - } - @Override public Set getActiveContexts() { return SubjectData.GLOBAL_CONTEXT; diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsService.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsService.java index 5bb957db9..ffcffe35b 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsService.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsService.java @@ -33,6 +33,7 @@ import me.lucko.luckperms.api.sponge.simple.persisted.SimplePersistedCollection; import me.lucko.luckperms.api.sponge.simple.persisted.SubjectStorage; import me.lucko.luckperms.contexts.SpongeCalculatorLink; import org.spongepowered.api.plugin.PluginContainer; +import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.context.ContextCalculator; import org.spongepowered.api.service.permission.*; import org.spongepowered.api.text.Text; @@ -41,6 +42,7 @@ import org.spongepowered.api.util.Tristate; import java.io.File; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** * The LuckPerms implementation of the Sponge Permission Service @@ -140,6 +142,36 @@ public class LuckPermsService implements PermissionService { plugin.getContextManager().registerCalculator(new SpongeCalculatorLink(contextCalculator)); } + public static Map convertContexts(Set contexts) { + return contexts.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); + } + + public static Set convertContexts(Map contexts) { + return contexts.entrySet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet()); + } + + public static Tristate convertTristate(me.lucko.luckperms.api.Tristate tristate) { + switch (tristate) { + case TRUE: + return Tristate.TRUE; + case FALSE: + return Tristate.FALSE; + default: + return Tristate.UNDEFINED; + } + } + + public static me.lucko.luckperms.api.Tristate convertTristate(Tristate tristate) { + switch (tristate) { + case TRUE: + return me.lucko.luckperms.api.Tristate.TRUE; + case FALSE: + return me.lucko.luckperms.api.Tristate.FALSE; + default: + return me.lucko.luckperms.api.Tristate.UNDEFINED; + } + } + @RequiredArgsConstructor @EqualsAndHashCode @ToString diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubjectData.java index 377fa20bb..60534983a 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubjectData.java @@ -31,6 +31,7 @@ import me.lucko.luckperms.api.Node; import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectLacksException; +import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.users.User; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.permission.Subject; @@ -41,25 +42,34 @@ import java.util.*; import java.util.stream.Collectors; import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters; -import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters; @AllArgsConstructor public class LuckPermsSubjectData implements SubjectData { private final boolean enduring; - private final LuckPermsSubject superClass; private final LuckPermsService service; @Getter private final PermissionHolder holder; + private void objectSave(PermissionHolder t) { + service.getPlugin().doAsync(() -> { + if (t instanceof User) { + ((User) t).refreshPermissions(); + service.getPlugin().getDatastore().saveUser(((User) t)); + } + if (t instanceof Group) { + service.getPlugin().getDatastore().saveGroup(((Group) t)); + service.getPlugin().runUpdateTask(); + } + }); + } + @Override public Map, Map> getAllPermissions() { Map, Map> perms = new HashMap<>(); for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) { - Set contexts = n.getExtraContexts().entrySet().stream() - .map(entry -> new Context(entry.getKey(), entry.getValue())) - .collect(Collectors.toSet()); + Set contexts = LuckPermsService.convertContexts(n.getExtraContexts()); if (n.isServerSpecific()) { contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get())); @@ -84,8 +94,14 @@ public class LuckPermsSubjectData implements SubjectData { } @Override - public Map getPermissions(Set set) { - return getAllPermissions().getOrDefault(set, ImmutableMap.of()); + public Map getPermissions(Set contexts) { + ImmutableMap.Builder permissions = ImmutableMap.builder(); + + (enduring ? holder.getNodes() : holder.getTransientNodes()).stream() + .filter(n -> n.shouldApplyWithContext(LuckPermsService.convertContexts(contexts), true)) + .forEach(n -> permissions.put(n.getKey(), n.getValue())); + + return permissions.build(); } @Override @@ -105,7 +121,8 @@ public class LuckPermsSubjectData implements SubjectData { holder.unsetTransientPermission(builder.build()); } } catch (ObjectLacksException ignored) {} - superClass.objectSave(holder); + + objectSave(holder); return true; } @@ -123,7 +140,8 @@ public class LuckPermsSubjectData implements SubjectData { holder.setTransientPermission(builder.build()); } } catch (ObjectAlreadyHasException ignored) {} - superClass.objectSave(holder); + + objectSave(holder); return true; } @@ -134,15 +152,14 @@ public class LuckPermsSubjectData implements SubjectData { } else { holder.clearTransientNodes(); } - superClass.objectSave(holder); + objectSave(holder); return true; } @Override - public boolean clearPermissions(Set set) { - Map context = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); + public boolean clearPermissions(Set contexts) { List toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream() - .filter(node -> node.shouldApplyWithContext(context)) + .filter(node -> node.shouldApplyWithContext(LuckPermsService.convertContexts(contexts))) .collect(Collectors.toList()); toRemove.forEach(n -> { @@ -159,7 +176,7 @@ public class LuckPermsSubjectData implements SubjectData { service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false); } - superClass.objectSave(holder); + objectSave(holder); return !toRemove.isEmpty(); } @@ -172,9 +189,7 @@ public class LuckPermsSubjectData implements SubjectData { continue; } - Set contexts = n.getExtraContexts().entrySet().stream() - .map(entry -> new Context(entry.getKey(), entry.getValue())) - .collect(Collectors.toSet()); + Set contexts = LuckPermsService.convertContexts(n.getExtraContexts()); if (n.isServerSpecific()) { contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get())); @@ -200,14 +215,21 @@ public class LuckPermsSubjectData implements SubjectData { @Override public List getParents(Set contexts) { - return getAllParents().getOrDefault(contexts, ImmutableList.of()); + ImmutableList.Builder parents = ImmutableList.builder(); + + (enduring ? holder.getNodes() : holder.getTransientNodes()).stream() + .filter(Node::isGroupNode) + .filter(n -> n.shouldApplyWithContext(LuckPermsService.convertContexts(contexts), true)) + .forEach(n -> parents.add(service.getGroupSubjects().get(n.getGroupName()))); + + return parents.build(); } @Override public boolean addParent(Set set, Subject subject) { - if (subject instanceof LuckPermsSubject) { - LuckPermsSubject permsSubject = ((LuckPermsSubject) subject); - Map contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); + if (subject instanceof LuckPermsGroupSubject) { + LuckPermsGroupSubject permsSubject = ((LuckPermsGroupSubject) subject); + Map contexts = LuckPermsService.convertContexts(set); try { if (enduring) { @@ -220,18 +242,18 @@ public class LuckPermsSubjectData implements SubjectData { .build()); } } catch (ObjectAlreadyHasException ignored) {} - superClass.objectSave(holder); - } else { - return false; + + objectSave(holder); + return true; } return false; } @Override public boolean removeParent(Set set, Subject subject) { - if (subject instanceof LuckPermsSubject) { - LuckPermsSubject permsSubject = ((LuckPermsSubject) subject); - Map contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); + if (subject instanceof LuckPermsGroupSubject) { + LuckPermsGroupSubject permsSubject = ((LuckPermsGroupSubject) subject); + Map contexts = LuckPermsService.convertContexts(set); try { if (enduring) { @@ -244,9 +266,9 @@ public class LuckPermsSubjectData implements SubjectData { .build()); } } catch (ObjectLacksException ignored) {} - superClass.objectSave(holder); - } else { - return false; + + objectSave(holder); + return true; } return false; } @@ -271,13 +293,14 @@ public class LuckPermsSubjectData implements SubjectData { service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false); } - superClass.objectSave(holder); + objectSave(holder); return !toRemove.isEmpty(); } @Override public boolean clearParents(Set set) { - Map context = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); + Map context = LuckPermsService.convertContexts(set); + List toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream() .filter(Node::isGroupNode) .filter(node -> node.shouldApplyWithContext(context)) @@ -297,7 +320,7 @@ public class LuckPermsSubjectData implements SubjectData { service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false); } - superClass.objectSave(holder); + objectSave(holder); return !toRemove.isEmpty(); } @@ -305,14 +328,19 @@ public class LuckPermsSubjectData implements SubjectData { public Map, Map> getAllOptions() { Map, Map> options = new HashMap<>(); + int prefixPriority = Integer.MIN_VALUE; + int suffixPriority = Integer.MIN_VALUE; + for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) { - if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) { + if (!n.getValue()) { continue; } - Set contexts = n.getExtraContexts().entrySet().stream() - .map(entry -> new Context(entry.getKey(), entry.getValue())) - .collect(Collectors.toSet()); + if (!n.isMeta() || !n.isPrefix() || n.isSuffix()) { + continue; + } + + Set contexts = LuckPermsService.convertContexts(n.getExtraContexts()); if (n.isServerSpecific()) { contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get())); @@ -326,7 +354,28 @@ public class LuckPermsSubjectData implements SubjectData { options.put(contexts, new HashMap<>()); } - options.get(contexts).put(unescapeCharacters(n.getMeta().getKey()), unescapeCharacters(n.getMeta().getValue())); + if (n.isPrefix()) { + Map.Entry value = n.getPrefix(); + if (value.getKey() > prefixPriority) { + options.get(contexts).put("prefix", value.getValue()); + prefixPriority = value.getKey(); + } + continue; + } + + if (n.isSuffix()) { + Map.Entry value = n.getSuffix(); + if (value.getKey() > suffixPriority) { + options.get(contexts).put("suffix", value.getValue()); + suffixPriority = value.getKey(); + } + continue; + } + + if (n.isMeta()) { + Map.Entry meta = n.getMeta(); + options.get(contexts).put(meta.getKey(), meta.getValue()); + } } ImmutableMap.Builder, Map> map = ImmutableMap.builder(); @@ -338,15 +387,55 @@ public class LuckPermsSubjectData implements SubjectData { @Override public Map getOptions(Set set) { - return getAllOptions().getOrDefault(set, Collections.emptyMap()); + ImmutableMap.Builder options = ImmutableMap.builder(); + Map contexts = LuckPermsService.convertContexts(set); + + int prefixPriority = Integer.MIN_VALUE; + int suffixPriority = Integer.MIN_VALUE; + + for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) { + if (!n.getValue()) { + continue; + } + + if (!n.isMeta() || !n.isPrefix() || n.isSuffix()) { + continue; + } + + if (!n.shouldApplyWithContext(contexts, true)) { + continue; + } + + if (n.isPrefix()) { + Map.Entry value = n.getPrefix(); + if (value.getKey() > prefixPriority) { + options.put("prefix", value.getValue()); + prefixPriority = value.getKey(); + } + continue; + } + + if (n.isSuffix()) { + Map.Entry value = n.getSuffix(); + if (value.getKey() > suffixPriority) { + options.put("suffix", value.getValue()); + suffixPriority = value.getKey(); + } + continue; + } + + if (n.isMeta()) { + Map.Entry meta = n.getMeta(); + options.put(meta.getKey(), meta.getValue()); + } + } + + return options.build(); } @Override public boolean setOption(Set set, String key, String value) { - Map context = new HashMap<>(); - for (Context c : set) { - context.put(c.getKey(), c.getValue()); - } + Map context = LuckPermsService.convertContexts(set); key = escapeCharacters(key); value = escapeCharacters(value); @@ -364,13 +453,14 @@ public class LuckPermsSubjectData implements SubjectData { ); } } catch (ObjectAlreadyHasException ignored) {} - superClass.objectSave(holder); + objectSave(holder); return true; } @Override public boolean clearOptions(Set set) { - Map context = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); + Map context = LuckPermsService.convertContexts(set); + List toRemove = (enduring ? holder.getNodes() : holder.getTransientNodes()).stream() .filter(n -> n.isMeta() || n.isPrefix() || n.isSuffix()) .filter(node -> node.shouldApplyWithContext(context)) @@ -386,7 +476,7 @@ public class LuckPermsSubjectData implements SubjectData { } catch (ObjectLacksException ignored) {} }); - superClass.objectSave(holder); + objectSave(holder); return !toRemove.isEmpty(); } @@ -406,7 +496,7 @@ public class LuckPermsSubjectData implements SubjectData { } catch (ObjectLacksException ignored) {} }); - superClass.objectSave(holder); + objectSave(holder); return !toRemove.isEmpty(); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsUserSubject.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsUserSubject.java index 4e8ff58d6..39d421eab 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsUserSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsUserSubject.java @@ -22,24 +22,26 @@ package me.lucko.luckperms.api.sponge; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; import me.lucko.luckperms.api.Contexts; -import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent; -import me.lucko.luckperms.api.implementation.internal.UserLink; +import me.lucko.luckperms.caching.MetaData; import me.lucko.luckperms.users.User; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.entity.living.player.Player; 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.*; -import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -public class LuckPermsUserSubject extends LuckPermsSubject { +@EqualsAndHashCode(of = "user") +public class LuckPermsUserSubject implements Subject { public static LuckPermsUserSubject wrapUser(User user, LuckPermsService service) { return new LuckPermsUserSubject(user, service); } @@ -47,92 +49,45 @@ public class LuckPermsUserSubject extends LuckPermsSubject { @Getter private User user; + private LuckPermsService service; + @Getter - private Map, ContextData> contextData; + private LuckPermsSubjectData subjectData; + + @Getter + private LuckPermsSubjectData transientSubjectData; private LuckPermsUserSubject(User user, LuckPermsService service) { - super(user, service); this.user = user; - - contextData = new ConcurrentHashMap<>(); + this.service = service; + this.subjectData = new LuckPermsSubjectData(true, service, user); + this.transientSubjectData = new LuckPermsSubjectData(false, service, user); } - @Override public void deprovision() { /* For some reason, Sponge holds onto User instances in a cache, which in turn, prevents LuckPerms data from being GCed. As well as unloading, we also remove all references to the User instances. */ - super.deprovision(); user = null; - contextData = null; + service = null; + subjectData = null; + transientSubjectData = null; } - @Override - public Tristate getPermissionValue(@NonNull Set contexts, @NonNull String permission) { - Map context = contexts.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); - ContextData cd = contextData.computeIfAbsent(context, map -> calculatePermissions(map, false)); - - me.lucko.luckperms.api.Tristate t = cd.getPermissionValue(permission); - if (t != me.lucko.luckperms.api.Tristate.UNDEFINED) { - return Tristate.fromBoolean(t.asBoolean()); - } else { - return Tristate.UNDEFINED; - } - } - - public ContextData calculatePermissions(Map context, boolean apply) { - Map toApply = user.exportNodes( - new Contexts( - context, - service.getPlugin().getConfiguration().isIncludingGlobalPerms(), - service.getPlugin().getConfiguration().isIncludingGlobalWorldPerms(), - true, - service.getPlugin().getConfiguration().isApplyingGlobalGroups(), - service.getPlugin().getConfiguration().isApplyingGlobalWorldGroups() - ), - Collections.emptyList(), - true + private Contexts calculateContexts(Set contexts) { + return new Contexts( + LuckPermsService.convertContexts(contexts), + service.getPlugin().getConfiguration().isIncludingGlobalPerms(), + service.getPlugin().getConfiguration().isIncludingGlobalWorldPerms(), + true, + service.getPlugin().getConfiguration().isApplyingGlobalGroups(), + service.getPlugin().getConfiguration().isApplyingGlobalWorldGroups() ); - - ContextData existing = contextData.get(context); - if (existing == null) { - existing = new ContextData(this, context, service); - if (apply) { - contextData.put(context, existing); - } - } - - boolean different = false; - if (toApply.size() != existing.getPermissionCache().size()) { - different = true; - } else { - for (Map.Entry e : existing.getPermissionCache().entrySet()) { - if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) { - continue; - } - different = true; - break; - } - } - - if (!different) return existing; - - existing.getPermissionCache().clear(); - existing.invalidateCache(); - existing.getPermissionCache().putAll(toApply); - service.getPlugin().getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(user))); - return existing; } - public void calculatePermissions(Set contexts, boolean apply) { - Map context = contexts.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); - calculatePermissions(context, apply); + private boolean hasData() { + return user.getUserData() != null; } - public void calculateActivePermissions(boolean apply) { - calculatePermissions(getActiveContexts(), apply); - } - - @Override public String getIdentifier() { return service.getPlugin().getUuidCache().getExternalUUID(user.getUuid()).toString(); @@ -150,6 +105,82 @@ public class LuckPermsUserSubject extends LuckPermsSubject { return Optional.empty(); } + @Override + public SubjectCollection getContainingCollection() { + return service.getUserSubjects(); + } + + @Override + public boolean hasPermission(Set contexts, String permission) { + return getPermissionValue(contexts, permission).asBoolean(); + } + + @Override + public Tristate getPermissionValue(@NonNull Set contexts, @NonNull String permission) { + if (hasData()) { + return LuckPermsService.convertTristate(user.getUserData().getPermissionData(calculateContexts(contexts)).getPermissionValue(permission)); + } + + return Tristate.UNDEFINED; + } + + @Override + public boolean isChildOf(@NonNull Set contexts, @NonNull Subject parent) { + return parent instanceof LuckPermsGroupSubject && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean(); + } + + @Override + public List getParents(Set contexts) { + List subjects = new ArrayList<>(); + + if (hasData()) { + for (String perm : user.getUserData().getPermissionData(calculateContexts(contexts)).getImmutableBacking().keySet()) { + if (!perm.startsWith("group.")) { + continue; + } + + String groupName = perm.substring("group.".length()); + if (service.getPlugin().getGroupManager().isLoaded(groupName)) { + subjects.add(service.getGroupSubjects().get(groupName)); + } + } + } + + subjects.addAll(service.getUserSubjects().getDefaults().getParents(contexts)); + subjects.addAll(service.getDefaults().getParents(contexts)); + + return subjects; + } + + @Override + public Optional getOption(Set contexts, String s) { + if (hasData()) { + MetaData data = user.getUserData().getMetaData(calculateContexts(contexts)); + if (s.equalsIgnoreCase("prefix")) { + if (data.getPrefix() != null) { + return Optional.of(data.getPrefix()); + } + } + + if (s.equalsIgnoreCase("suffix")) { + if (data.getSuffix() != null) { + return Optional.of(data.getSuffix()); + } + } + + if (data.getMeta().containsKey(s)) { + return Optional.of(data.getMeta().get(s)); + } + } + + Optional v = service.getUserSubjects().getDefaults().getOption(contexts, s); + if (v.isPresent()) { + return v; + } + + return service.getDefaults().getOption(contexts, s); + } + @Override public Set getActiveContexts() { final UUID uuid = service.getPlugin().getUuidCache().getExternalUUID(user.getUuid()); diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/GroupCollection.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/GroupCollection.java index ac50d00ca..e9b34f674 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/GroupCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/GroupCollection.java @@ -24,7 +24,7 @@ package me.lucko.luckperms.api.sponge.collections; import lombok.NonNull; import me.lucko.luckperms.api.sponge.LuckPermsService; -import me.lucko.luckperms.api.sponge.LuckPermsSubject; +import me.lucko.luckperms.api.sponge.LuckPermsGroupSubject; import me.lucko.luckperms.api.sponge.simple.SimpleCollection; import me.lucko.luckperms.groups.GroupManager; import org.spongepowered.api.service.context.Context; @@ -56,7 +56,7 @@ public class GroupCollection implements SubjectCollection { @Override public Subject get(@NonNull String id) { if (manager.isLoaded(id)) { - return LuckPermsSubject.wrapHolder(manager.get(id), service); + return LuckPermsGroupSubject.wrapGroup(manager.get(id), service); } return fallback.get(id); @@ -70,7 +70,7 @@ public class GroupCollection implements SubjectCollection { @Override public Iterable getAllSubjects() { return manager.getAll().values().stream() - .map(u -> LuckPermsSubject.wrapHolder(u, service)) + .map(u -> LuckPermsGroupSubject.wrapGroup(u, service)) .collect(Collectors.toList()); } @@ -82,7 +82,7 @@ public class GroupCollection implements SubjectCollection { @Override public Map getAllWithPermission(@NonNull Set contexts, @NonNull String node) { return manager.getAll().values().stream() - .map(u -> LuckPermsSubject.wrapHolder(u, service)) + .map(u -> LuckPermsGroupSubject.wrapGroup(u, service)) .filter(sub -> sub.isPermissionSet(contexts, node)) .collect(Collectors.toMap(sub -> sub, sub -> sub.getPermissionValue(contexts, node).asBoolean())); } diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/simple/persisted/SimpleSubjectDataHolder.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/simple/persisted/SimpleSubjectDataHolder.java index 34dd77837..c7ccf6629 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/simple/persisted/SimpleSubjectDataHolder.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/simple/persisted/SimpleSubjectDataHolder.java @@ -30,6 +30,8 @@ import org.spongepowered.api.util.Tristate; import java.util.*; import java.util.stream.Collectors; +import static me.lucko.luckperms.api.sponge.LuckPermsService.convertContexts; + /** * Holds SubjectData in a "gson friendly" format for serialization */ @@ -92,12 +94,4 @@ public class SimpleSubjectDataHolder { } } } - - public static Map convertContexts(Set contexts) { - return contexts.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); - } - - public static Set convertContexts(Map contexts) { - return contexts.entrySet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet()); - } } diff --git a/sponge/src/main/java/me/lucko/luckperms/calculators/DefaultsProcessor.java b/sponge/src/main/java/me/lucko/luckperms/calculators/DefaultsProcessor.java index 7a5c2d4d1..1f04eb8cd 100644 --- a/sponge/src/main/java/me/lucko/luckperms/calculators/DefaultsProcessor.java +++ b/sponge/src/main/java/me/lucko/luckperms/calculators/DefaultsProcessor.java @@ -25,9 +25,12 @@ package me.lucko.luckperms.calculators; import lombok.AllArgsConstructor; import me.lucko.luckperms.api.sponge.LuckPermsService; import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.util.Tristate; import java.util.Set; +import static me.lucko.luckperms.api.sponge.LuckPermsService.convertTristate; + @AllArgsConstructor public class DefaultsProcessor implements PermissionProcessor { private final LuckPermsService service; @@ -35,11 +38,16 @@ public class DefaultsProcessor implements PermissionProcessor { @Override public me.lucko.luckperms.api.Tristate hasPermission(String permission) { - org.spongepowered.api.util.Tristate t = service.getDefaults().getPermissionValue(contexts, permission); - if (t != org.spongepowered.api.util.Tristate.UNDEFINED) { - return me.lucko.luckperms.api.Tristate.fromBoolean(t.asBoolean()); - } else { - return me.lucko.luckperms.api.Tristate.UNDEFINED; + Tristate t = service.getUserSubjects().getDefaults().getPermissionValue(contexts, permission); + if (t != Tristate.UNDEFINED) { + return convertTristate(Tristate.fromBoolean(t.asBoolean())); } + + Tristate t2 = service.getDefaults().getPermissionValue(contexts, permission); + if (t2 != Tristate.UNDEFINED) { + return convertTristate(Tristate.fromBoolean(t.asBoolean())); + } + + return me.lucko.luckperms.api.Tristate.UNDEFINED; } } diff --git a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java deleted file mode 100644 index 641ba65c2..000000000 --- a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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.users; - -import me.lucko.luckperms.LPSpongePlugin; -import me.lucko.luckperms.api.sponge.LuckPermsUserSubject; -import me.lucko.luckperms.api.sponge.collections.UserCollection; - -import java.util.UUID; - -class SpongeUser extends User { - private final LPSpongePlugin plugin; - - SpongeUser(UUID uuid, LPSpongePlugin plugin) { - super(uuid, plugin); - this.plugin = plugin; - } - - SpongeUser(UUID uuid, String username, LPSpongePlugin plugin) { - super(uuid, username, plugin); - this.plugin = plugin; - } - - @Override - public synchronized void refreshPermissions() { - UserCollection uc = plugin.getService().getUserSubjects(); - if (!uc.getUsers().containsKey(getUuid())) { - return; - } - - LuckPermsUserSubject us = uc.getUsers().get(getUuid()); - us.calculateActivePermissions(true); - } -} diff --git a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java deleted file mode 100644 index 2cc083cc4..000000000 --- a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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.users; - -import me.lucko.luckperms.LPSpongePlugin; -import me.lucko.luckperms.api.context.ContextListener; -import org.spongepowered.api.entity.living.player.Player; - -import java.util.Map; - -public class SpongeUserManager extends UserManager implements ContextListener { - private final LPSpongePlugin plugin; - - public SpongeUserManager(LPSpongePlugin plugin) { - super(plugin); - this.plugin = plugin; - } - - @Override - public void cleanup(User user) { - if (!plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())).isPresent()) { - unload(user); - } - } - - @Override - public void preUnload(User user) { - plugin.getService().getUserSubjects().unload(user.getUuid()); - } - - @Override - public User apply(UserIdentifier id) { - SpongeUser user = id.getUsername() == null ? - new SpongeUser(id.getUuid(), plugin) : - new SpongeUser(id.getUuid(), id.getUsername(), plugin); - return user; - } - - @Override - public void updateAllUsers() { - plugin.getGame().getServer().getOnlinePlayers().stream() - .map(p -> plugin.getUuidCache().getUUID(p.getUniqueId())) - .forEach(u -> plugin.getDatastore().loadUser(u, "null")); - } - - @Override - public void onContextChange(Player subject, Map.Entry before, Map.Entry current) throws Exception { - // Not needed on Sponge. The context is accumulated on each permission check. - } -}