diff --git a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java index 43bf60f51..106921758 100644 --- a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java +++ b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java @@ -22,6 +22,8 @@ package me.lucko.luckperms.api; +import me.lucko.luckperms.api.context.ContextListener; +import me.lucko.luckperms.api.context.IContextCalculator; import me.lucko.luckperms.api.event.LPListener; import java.util.Optional; @@ -218,4 +220,18 @@ public interface LuckPermsApi { */ Node.Builder buildNode(String permission) throws IllegalArgumentException; + /** + * Register a custom context calculator to the server + * @param contextCalculator the context calculator to register. The type MUST be the player class of the platform. + * @throws ClassCastException if the type is not the player class of the platform. + */ + void registerContextCalculator(IContextCalculator contextCalculator); + + /** + * Registers a custom context listener to the server, + * @param contextListener the context listener to register. The type MUST be the player class of the platform. + * @throws ClassCastException if the type is not the player class of the platform. + */ + void registerContextListener(ContextListener contextListener); + } diff --git a/api/src/main/java/me/lucko/luckperms/api/Node.java b/api/src/main/java/me/lucko/luckperms/api/Node.java index 5d9f9f805..f521cf747 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Node.java +++ b/api/src/main/java/me/lucko/luckperms/api/Node.java @@ -105,7 +105,15 @@ public interface Node extends Map.Entry { boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex); /** - * If this node should apply given the specific context + * If this node should apply in the given context + * @param context the context key value pairs + * @param worldAndServer if world and server contexts should be checked + * @return true if the node should apply + */ + boolean shouldApplyWithContext(Map context, boolean worldAndServer); + + /** + * If this node should apply in the given context * @param context the context key value pairs * @return true if the node should apply */ diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ContextCalculator.java b/api/src/main/java/me/lucko/luckperms/api/context/ContextCalculator.java new file mode 100644 index 000000000..eca3d7260 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/context/ContextCalculator.java @@ -0,0 +1,74 @@ +/* + * 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.context; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * A simple implementation of the listener aspects of {@link IContextCalculator} + * @param the subject type + */ +public abstract class ContextCalculator implements IContextCalculator { + private final List> listeners = new CopyOnWriteArrayList<>(); + + /** + * Pushes an update to all registered {@link ContextListener}s. + * Make sure any changes are applied internally before this method is called. + * @param subject the subject that changed + * @param before the context state before the change + * @param current the context state after the change (now) + * @throws NullPointerException if any parameters are null + */ + protected void pushUpdate(T subject, Map.Entry before, Map.Entry current) { + if (subject == null) { + throw new NullPointerException("subject"); + } + if (before == null) { + throw new NullPointerException("before"); + } + if (current == null) { + throw new NullPointerException("current"); + } + + for (ContextListener listener : listeners) { + try { + listener.onContextChange(subject, before, current); + } catch (Exception e) { + System.out.println("Exception whilst passing context change to listener: " + listener); + e.printStackTrace(); + } + } + } + + @Override + public void addListener(ContextListener listener) { + if (listener == null) { + throw new NullPointerException("listener"); + } + + listeners.add(listener); + } + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ContextListener.java b/api/src/main/java/me/lucko/luckperms/api/context/ContextListener.java new file mode 100644 index 000000000..03882c72b --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/context/ContextListener.java @@ -0,0 +1,41 @@ +/* + * 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.context; + +import java.util.Map; + +/** + * Listens to context changes + * @param the subject type, Is ALWAYS the player class of the platform. + */ +public interface ContextListener { + + /** + * Called whenever a context changes on the + * @param subject the subject that had context changed + * @param before the context state before the change + * @param current the context state after the change (now) + */ + void onContextChange(T subject, Map.Entry before, Map.Entry current) throws Exception; + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/context/IContextCalculator.java b/api/src/main/java/me/lucko/luckperms/api/context/IContextCalculator.java new file mode 100644 index 000000000..38aa7bd05 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/context/IContextCalculator.java @@ -0,0 +1,58 @@ +/* + * 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.context; + +import java.util.Map; + +/** + * Calculates whether contexts are applicable to {@link T} + * + *

Somewhat inspired by the system used on Sponge. + * @param the subject type. Is ALWAYS the player class of the platform. + */ +public interface IContextCalculator { + + /** + * Gives the subject all of the applicable contexts they meet + * @param subject the subject to add contexts tp + * @param accumulator a map of contexts to add to + * @return the map + */ + Map giveApplicableContext(T subject, Map accumulator); + + /** + * Checks to see if a context is applicable to a subject + * @param subject the subject to check against + * @param context the context to check for + * @return true if met, or false if not. If this calculator does not calculate the given context, return false. + */ + boolean isContextApplicable(T subject, Map.Entry context); + + /** + * Adds a listener to be called whenever a context handled by this calculator changes + * @param listener the listener instance + * @throws NullPointerException if listener is null + */ + void addListener(ContextListener listener); + +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/BukkitConfig.java b/bukkit/src/main/java/me/lucko/luckperms/BukkitConfig.java index 5c5113fe2..e1a780edf 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/BukkitConfig.java +++ b/bukkit/src/main/java/me/lucko/luckperms/BukkitConfig.java @@ -22,7 +22,7 @@ package me.lucko.luckperms; -import me.lucko.luckperms.core.AbstractConfiguration; +import me.lucko.luckperms.config.AbstractConfiguration; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; diff --git a/bukkit/src/main/java/me/lucko/luckperms/BukkitListener.java b/bukkit/src/main/java/me/lucko/luckperms/BukkitListener.java index bd89b14ea..e9ba85695 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/BukkitListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/BukkitListener.java @@ -84,21 +84,13 @@ class BukkitListener extends AbstractListener implements Listener { public void onPlayerJoin(PlayerJoinEvent e) { // Refresh permissions again UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()); - plugin.getUserManager().getWorldCache().put(internal, e.getPlayer().getWorld().getName()); + plugin.getWorldCalculator().getWorldCache().put(internal, e.getPlayer().getWorld().getName()); plugin.doAsync(() -> refreshPlayer(internal)); } - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerChangedWorld(PlayerChangedWorldEvent e) { - UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()); - plugin.getUserManager().getWorldCache().put(internal, e.getPlayer().getWorld().getName()); - plugin.doAsync(() -> refreshPlayer(internal)); - - } - @EventHandler(priority = EventPriority.LOWEST) public void onPlayerQuit(PlayerQuitEvent e) { - plugin.getUserManager().getWorldCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId())); + plugin.getWorldCalculator().getWorldCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId())); onLeave(e.getPlayer().getUniqueId()); } diff --git a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java index edf1b532d..4041c5a50 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java @@ -30,8 +30,11 @@ import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.api.vault.VaultHook; 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.core.LPConfiguration; +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; @@ -72,6 +75,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { private ConsecutiveExecutor consecutiveExecutor; private DefaultsProvider defaultsProvider; private LocaleManager localeManager; + private ContextManager contextManager; + private WorldCalculator worldCalculator; @Override public void onEnable() { @@ -116,6 +121,13 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { importer = new Importer(commandManager); consecutiveExecutor = new ConsecutiveExecutor(commandManager); + 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) { long ticks = mins * 60 * 20; 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 ed5cc09e1..a4941b40c 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 @@ -26,11 +26,11 @@ import lombok.NonNull; import lombok.Setter; import me.lucko.luckperms.LPBukkitPlugin; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.users.User; -import me.lucko.luckperms.utils.Contexts; import net.milkbowl.vault.chat.Chat; import java.util.Iterator; @@ -90,7 +90,7 @@ public class VaultChatHook extends Chat { } } - Node.Builder metaNode = new me.lucko.luckperms.utils.Node.Builder("meta." + node + "." + value).setValue(true); + Node.Builder metaNode = new me.lucko.luckperms.core.Node.Builder("meta." + node + "." + value).setValue(true); if (!perms.getServer().equalsIgnoreCase("global")) { metaNode.setServer(perms.getServer()); } @@ -140,7 +140,7 @@ public class VaultChatHook extends Chat { if (holder == null) return; if (value.equals("")) return; - Node.Builder node = new me.lucko.luckperms.utils.Node.Builder(prefix ? "prefix" : "suffix" + ".1000." + escapeCharacters(value)); + Node.Builder node = new me.lucko.luckperms.core.Node.Builder(prefix ? "prefix" : "suffix" + ".1000." + escapeCharacters(value)); node.setValue(true); if (!perms.getServer().equalsIgnoreCase("global")) { node.setServer(perms.getServer()); diff --git a/bukkit/src/main/java/me/lucko/luckperms/contexts/WorldCalculator.java b/bukkit/src/main/java/me/lucko/luckperms/contexts/WorldCalculator.java new file mode 100644 index 000000000..7b1a3432e --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/contexts/WorldCalculator.java @@ -0,0 +1,90 @@ +/* + * 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.contexts; + +import com.google.common.collect.Maps; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.api.context.ContextCalculator; +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.PlayerChangedWorldEvent; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@RequiredArgsConstructor +public class WorldCalculator extends ContextCalculator implements Listener { + private static final String WORLD_KEY = "world"; + + private final LuckPermsPlugin plugin; + + @Getter + private final Map worldCache = new ConcurrentHashMap<>(); + + @Override + public Map giveApplicableContext(Player subject, Map accumulator) { + String world = getWorld(subject); + + if (world != null) { + accumulator.put(WORLD_KEY, world); + } + + return accumulator; + } + + @Override + public boolean isContextApplicable(Player subject, Map.Entry context) { + if (!context.getKey().equals(WORLD_KEY)) { + return false; + } + + String world = getWorld(subject); + return world != null && world.equals(context.getValue()); + } + + private String getWorld(Player player) { + UUID internal = plugin.getUuidCache().getUUID(player.getUniqueId()); + if (!worldCache.containsKey(internal)) { + return null; + } + + String world = worldCache.get(internal); + return plugin.getConfiguration().getWorldRewrites().getOrDefault(world, world); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerChangedWorld(PlayerChangedWorldEvent e) { + UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()); + worldCache.put(internal, e.getPlayer().getWorld().getName()); + pushUpdate( + e.getPlayer(), + Maps.immutableEntry(WORLD_KEY, e.getFrom().getName()), + Maps.immutableEntry(WORLD_KEY, e.getPlayer().getWorld().getName()) + ); + } +} 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 139abd5de..ac13a8861 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/inject/LPPermissible.java +++ b/bukkit/src/main/java/me/lucko/luckperms/inject/LPPermissible.java @@ -27,8 +27,8 @@ import lombok.Getter; import lombok.NonNull; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.api.Tristate; -import me.lucko.luckperms.utils.PermissionCalculator; -import me.lucko.luckperms.utils.PermissionProcessor; +import me.lucko.luckperms.calculators.PermissionCalculator; +import me.lucko.luckperms.calculators.PermissionProcessor; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.permissions.*; diff --git a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java index 32e798cef..10617a743 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java +++ b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java @@ -27,11 +27,13 @@ import lombok.Setter; import me.lucko.luckperms.LPBukkitPlugin; import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent; import me.lucko.luckperms.api.implementation.internal.UserLink; +import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.inject.LPPermissible; -import me.lucko.luckperms.utils.Contexts; +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; @@ -60,17 +62,14 @@ public class BukkitUser extends User { } // Calculate the permissions that should be applied. This is done async, who cares about how long it takes or how often it's done. - String world = plugin.getUserManager().getWorldCache().get(getUuid()); Map toApply = exportNodes( new Contexts( - getPlugin().getConfiguration().getServer(), - getPlugin().getConfiguration().getWorldRewrites().getOrDefault(world, world), - null, - getPlugin().getConfiguration().isIncludingGlobalPerms(), - getPlugin().getConfiguration().isIncludingGlobalWorldPerms(), + plugin.getContextManager().giveApplicableContext((Player) lpPermissible.getParent(), new HashMap<>()), + plugin.getConfiguration().isIncludingGlobalPerms(), + plugin.getConfiguration().isIncludingGlobalWorldPerms(), true, - getPlugin().getConfiguration().isApplyingGlobalGroups(), - getPlugin().getConfiguration().isApplyingGlobalWorldGroups() + plugin.getConfiguration().isApplyingGlobalGroups(), + plugin.getConfiguration().isApplyingGlobalWorldGroups() ), Collections.emptyList() ); diff --git a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java index d5d449a28..33cad0451 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java +++ b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java @@ -22,23 +22,19 @@ package me.lucko.luckperms.users; -import lombok.Getter; 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.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -public class BukkitUserManager extends UserManager { +public class BukkitUserManager extends UserManager implements ContextListener { private final LPBukkitPlugin plugin; - @Getter - private final Map worldCache = new ConcurrentHashMap<>(); - public BukkitUserManager(LPBukkitPlugin plugin) { super(plugin); this.plugin = plugin; @@ -89,4 +85,14 @@ public class BukkitUserManager extends UserManager { 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/BungeeConfig.java b/bungee/src/main/java/me/lucko/luckperms/BungeeConfig.java index 791b9cab6..bc9e97eaa 100644 --- a/bungee/src/main/java/me/lucko/luckperms/BungeeConfig.java +++ b/bungee/src/main/java/me/lucko/luckperms/BungeeConfig.java @@ -22,7 +22,7 @@ package me.lucko.luckperms; -import me.lucko.luckperms.core.AbstractConfiguration; +import me.lucko.luckperms.config.AbstractConfiguration; import net.md_5.bungee.config.Configuration; import net.md_5.bungee.config.ConfigurationProvider; import net.md_5.bungee.config.YamlConfiguration; diff --git a/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java b/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java index fd43a7cba..efe88a0e7 100644 --- a/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java +++ b/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java @@ -118,9 +118,4 @@ public class BungeeListener extends AbstractListener implements Listener { plugin.getPlayerCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId())); onLeave(e.getPlayer().getUniqueId()); } - - @EventHandler - public void onPlayerServerSwitch(ServerSwitchEvent e) { - refreshPlayer(e.getPlayer().getUniqueId()); - } } diff --git a/bungee/src/main/java/me/lucko/luckperms/BungeePlayerCache.java b/bungee/src/main/java/me/lucko/luckperms/BungeePlayerCache.java index d123076bc..6c4d0a161 100644 --- a/bungee/src/main/java/me/lucko/luckperms/BungeePlayerCache.java +++ b/bungee/src/main/java/me/lucko/luckperms/BungeePlayerCache.java @@ -23,8 +23,8 @@ package me.lucko.luckperms; import lombok.Getter; -import me.lucko.luckperms.utils.PermissionCalculator; -import me.lucko.luckperms.utils.PermissionProcessor; +import me.lucko.luckperms.calculators.PermissionCalculator; +import me.lucko.luckperms.calculators.PermissionProcessor; import java.util.ArrayList; import java.util.List; diff --git a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java index 788e6b530..f596c9779 100644 --- a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java @@ -29,9 +29,12 @@ import me.lucko.luckperms.api.implementation.ApiProvider; 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.core.LPConfiguration; +import me.lucko.luckperms.contexts.BackendServerCalculator; +import me.lucko.luckperms.contexts.ContextManager; +import me.lucko.luckperms.contexts.ServerCalculator; import me.lucko.luckperms.core.UuidCache; import me.lucko.luckperms.data.Importer; import me.lucko.luckperms.groups.GroupManager; @@ -41,7 +44,6 @@ 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.connection.ProxiedPlayer; @@ -58,7 +60,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { private final Map playerCache = new ConcurrentHashMap<>(); private final Set ignoringLogs = ConcurrentHashMap.newKeySet(); private LPConfiguration configuration; - private UserManager userManager; + private BungeeUserManager userManager; private GroupManager groupManager; private TrackManager trackManager; private Datastore datastore; @@ -68,6 +70,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { private Importer importer; private ConsecutiveExecutor consecutiveExecutor; private LocaleManager localeManager; + private ContextManager contextManager; @Override public void onEnable() { @@ -108,6 +111,13 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { importer = new Importer(commandManager); consecutiveExecutor = new ConsecutiveExecutor(commandManager); + 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) { getProxy().getScheduler().schedule(this, new UpdateTask(this), mins, mins, TimeUnit.MINUTES); diff --git a/bungee/src/main/java/me/lucko/luckperms/contexts/BackendServerCalculator.java b/bungee/src/main/java/me/lucko/luckperms/contexts/BackendServerCalculator.java new file mode 100644 index 000000000..ece40d89b --- /dev/null +++ b/bungee/src/main/java/me/lucko/luckperms/contexts/BackendServerCalculator.java @@ -0,0 +1,66 @@ +/* + * 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.contexts; + +import com.google.common.collect.Maps; +import me.lucko.luckperms.api.context.ContextCalculator; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.ServerSwitchEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; + +import java.util.Map; + +public class BackendServerCalculator extends ContextCalculator implements Listener { + private static final String WORLD_KEY = "world"; + + @Override + public Map giveApplicableContext(ProxiedPlayer subject, Map accumulator) { + String server = getServer(subject); + + if (server != null) { + accumulator.put(WORLD_KEY, server); + } + + return accumulator; + } + + @Override + public boolean isContextApplicable(ProxiedPlayer subject, Map.Entry context) { + if (!context.getKey().equals(WORLD_KEY)) { + return false; + } + + String server = getServer(subject); + return server != null && server.equals(context.getValue()); + } + + @EventHandler + public void onPlayerServerSwitch(ServerSwitchEvent e) { + pushUpdate(e.getPlayer(), Maps.immutableEntry("null", "null"), Maps.immutableEntry(WORLD_KEY, getServer(e.getPlayer()))); + } + + private static String getServer(ProxiedPlayer player) { + return player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName()); + } +} diff --git a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java index e67fedf7c..8b9f079e6 100644 --- a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java +++ b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java @@ -26,10 +26,11 @@ import me.lucko.luckperms.BungeePlayerCache; import me.lucko.luckperms.LPBungeePlugin; import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent; import me.lucko.luckperms.api.implementation.internal.UserLink; -import me.lucko.luckperms.utils.Contexts; +import me.lucko.luckperms.contexts.Contexts; import net.md_5.bungee.api.connection.ProxiedPlayer; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -58,14 +59,10 @@ public class BungeeUser extends User { return; } - final String server = player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName()); - // Calculate the permissions that should be applied. This is done async. Map toApply = exportNodes( new Contexts( - plugin.getConfiguration().getServer(), - plugin.getConfiguration().getWorldRewrites().getOrDefault(server, server), - null, + plugin.getContextManager().giveApplicableContext(player, new HashMap<>()), plugin.getConfiguration().isIncludingGlobalPerms(), plugin.getConfiguration().isIncludingGlobalWorldPerms(), true, diff --git a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java index fe1f3a012..1281efabb 100644 --- a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java +++ b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java @@ -23,10 +23,13 @@ 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 { +public class BungeeUserManager extends UserManager implements ContextListener { private final LPBungeePlugin plugin; public BungeeUserManager(LPBungeePlugin plugin) { @@ -57,4 +60,14 @@ public class BungeeUserManager extends UserManager { .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 7cae34202..349183d69 100644 --- a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java @@ -27,8 +27,9 @@ import me.lucko.luckperms.api.PlatformType; import me.lucko.luckperms.api.implementation.ApiProvider; 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.core.LPConfiguration; +import me.lucko.luckperms.contexts.ContextManager; import me.lucko.luckperms.core.UuidCache; import me.lucko.luckperms.data.Importer; import me.lucko.luckperms.groups.GroupManager; @@ -62,6 +63,7 @@ public interface LuckPermsPlugin { Importer getImporter(); ConsecutiveExecutor getConsecutiveExecutor(); LocaleManager getLocaleManager(); + ContextManager getContextManager(); /** * @return the version of the plugin diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java index e28a47cc0..4e70273d3 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java @@ -27,6 +27,8 @@ import lombok.AllArgsConstructor; import lombok.NonNull; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.api.*; +import me.lucko.luckperms.api.context.ContextListener; +import me.lucko.luckperms.api.context.IContextCalculator; import me.lucko.luckperms.api.event.LPEvent; import me.lucko.luckperms.api.event.LPListener; import me.lucko.luckperms.api.implementation.internal.*; @@ -208,6 +210,18 @@ public class ApiProvider implements LuckPermsApi { @Override public Node.Builder buildNode(@NonNull String permission) throws IllegalArgumentException { - return new me.lucko.luckperms.utils.Node.Builder(checkNode(permission)); + return new me.lucko.luckperms.core.Node.Builder(checkNode(permission)); + } + + @SuppressWarnings("unchecked") + @Override + public void registerContextCalculator(IContextCalculator contextCalculator) { + plugin.getContextManager().registerCalculator(contextCalculator); + } + + @SuppressWarnings("unchecked") + @Override + public void registerContextListener(ContextListener contextListener) { + plugin.getContextManager().registerListener(contextListener); } } diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java index 34af6efca..89a4b9639 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java @@ -30,11 +30,11 @@ import me.lucko.luckperms.api.data.MySQLConfiguration; import java.util.Map; /** - * Provides a link between {@link LPConfiguration} and {@link me.lucko.luckperms.core.LPConfiguration} + * Provides a link between {@link LPConfiguration} and {@link me.lucko.luckperms.config.LPConfiguration} */ @AllArgsConstructor public class LPConfigurationLink implements LPConfiguration { - private final me.lucko.luckperms.core.LPConfiguration master; + private final me.lucko.luckperms.config.LPConfiguration master; @Override public String getServer() { diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java index 8e085e7ca..132c14852 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java @@ -27,9 +27,9 @@ import lombok.NonNull; import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.PermissionHolder; import me.lucko.luckperms.api.Tristate; +import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectLacksException; -import me.lucko.luckperms.utils.Contexts; import java.util.*; @@ -253,7 +253,16 @@ public class PermissionHolderLink implements PermissionHolder { @Override public Map getPermissions(String server, String world, Map extraContext, boolean includeGlobal, List possibleNodes, boolean applyGroups) { - return master.exportNodes(new Contexts(server, world, extraContext, includeGlobal, includeGlobal, applyGroups, true, true), possibleNodes); + if (extraContext == null) { + extraContext = new HashMap<>(); + } + if (server != null && !server.equals("")) { + extraContext.put("server", server); + } + if (world != null && !world.equals("")) { + extraContext.put("world", world); + } + return master.exportNodes(new Contexts(extraContext, includeGlobal, includeGlobal, applyGroups, true, true), possibleNodes); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/utils/PermissionCalculator.java b/common/src/main/java/me/lucko/luckperms/calculators/PermissionCalculator.java similarity index 99% rename from common/src/main/java/me/lucko/luckperms/utils/PermissionCalculator.java rename to common/src/main/java/me/lucko/luckperms/calculators/PermissionCalculator.java index 6809ee4bd..afc6f9d9f 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/PermissionCalculator.java +++ b/common/src/main/java/me/lucko/luckperms/calculators/PermissionCalculator.java @@ -20,7 +20,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.utils; +package me.lucko.luckperms.calculators; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/common/src/main/java/me/lucko/luckperms/utils/PermissionProcessor.java b/common/src/main/java/me/lucko/luckperms/calculators/PermissionProcessor.java similarity index 96% rename from common/src/main/java/me/lucko/luckperms/utils/PermissionProcessor.java rename to common/src/main/java/me/lucko/luckperms/calculators/PermissionProcessor.java index 3616ad6b9..fbdb7b0f4 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/PermissionProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/calculators/PermissionProcessor.java @@ -20,7 +20,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.utils; +package me.lucko.luckperms.calculators; import me.lucko.luckperms.api.Tristate; diff --git a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupChatMeta.java b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupChatMeta.java index d3ab07d9d..b09abc181 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupChatMeta.java @@ -27,8 +27,8 @@ import me.lucko.luckperms.api.Node; import me.lucko.luckperms.commands.*; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Permission; +import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.groups.Group; -import me.lucko.luckperms.utils.Contexts; import java.util.*; diff --git a/common/src/main/java/me/lucko/luckperms/commands/misc/InfoCommand.java b/common/src/main/java/me/lucko/luckperms/commands/misc/InfoCommand.java index 3cca41477..1453dd2d0 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/misc/InfoCommand.java +++ b/common/src/main/java/me/lucko/luckperms/commands/misc/InfoCommand.java @@ -26,9 +26,9 @@ import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.commands.CommandResult; import me.lucko.luckperms.commands.Sender; import me.lucko.luckperms.commands.SingleMainCommand; +import me.lucko.luckperms.config.LPConfiguration; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Permission; -import me.lucko.luckperms.core.LPConfiguration; import java.util.List; diff --git a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserChatMeta.java b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserChatMeta.java index 395390b71..e77f50e33 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserChatMeta.java @@ -27,8 +27,8 @@ import me.lucko.luckperms.api.Node; import me.lucko.luckperms.commands.*; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Permission; +import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.users.User; -import me.lucko.luckperms.utils.Contexts; import java.util.AbstractMap.SimpleEntry; import java.util.List; diff --git a/common/src/main/java/me/lucko/luckperms/core/AbstractConfiguration.java b/common/src/main/java/me/lucko/luckperms/config/AbstractConfiguration.java similarity index 99% rename from common/src/main/java/me/lucko/luckperms/core/AbstractConfiguration.java rename to common/src/main/java/me/lucko/luckperms/config/AbstractConfiguration.java index bd567688a..eb7f0ca87 100644 --- a/common/src/main/java/me/lucko/luckperms/core/AbstractConfiguration.java +++ b/common/src/main/java/me/lucko/luckperms/config/AbstractConfiguration.java @@ -20,7 +20,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.core; +package me.lucko.luckperms.config; import com.google.common.collect.ImmutableMap; import lombok.AccessLevel; diff --git a/common/src/main/java/me/lucko/luckperms/core/LPConfiguration.java b/common/src/main/java/me/lucko/luckperms/config/LPConfiguration.java similarity index 98% rename from common/src/main/java/me/lucko/luckperms/core/LPConfiguration.java rename to common/src/main/java/me/lucko/luckperms/config/LPConfiguration.java index 264e8cb5c..14b97fffb 100644 --- a/common/src/main/java/me/lucko/luckperms/core/LPConfiguration.java +++ b/common/src/main/java/me/lucko/luckperms/config/LPConfiguration.java @@ -20,7 +20,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.core; +package me.lucko.luckperms.config; import me.lucko.luckperms.storage.DatastoreConfiguration; diff --git a/common/src/main/java/me/lucko/luckperms/contexts/ContextManager.java b/common/src/main/java/me/lucko/luckperms/contexts/ContextManager.java new file mode 100644 index 000000000..ba2d94cec --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/contexts/ContextManager.java @@ -0,0 +1,65 @@ +/* + * 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.contexts; + +import me.lucko.luckperms.api.context.ContextListener; +import me.lucko.luckperms.api.context.IContextCalculator; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +public class ContextManager { + + private final List> calculators = new CopyOnWriteArrayList<>(); + private final List> listeners = new CopyOnWriteArrayList<>(); + + public void registerCalculator(IContextCalculator calculator) { + listeners.forEach(calculator::addListener); + calculators.add(calculator); + } + + public void registerListener(ContextListener listener) { + for (IContextCalculator calculator : calculators) { + calculator.addListener(listener); + } + + listeners.add(listener); + } + + public Map giveApplicableContext(T subject, Map accumulator) { + for (IContextCalculator calculator : calculators) { + calculator.giveApplicableContext(subject, accumulator); + } + return accumulator; + } + + public boolean isContextApplicable(T subject, Map.Entry context) { + for (IContextCalculator calculator : calculators) { + if (calculator.isContextApplicable(subject, context)) { + return true; + } + } + return false; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/utils/Contexts.java b/common/src/main/java/me/lucko/luckperms/contexts/Contexts.java similarity index 67% rename from common/src/main/java/me/lucko/luckperms/utils/Contexts.java rename to common/src/main/java/me/lucko/luckperms/contexts/Contexts.java index f2752aa47..70ce86004 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/Contexts.java +++ b/common/src/main/java/me/lucko/luckperms/contexts/Contexts.java @@ -20,11 +20,10 @@ * SOFTWARE. */ -package me.lucko.luckperms.utils; +package me.lucko.luckperms.contexts; import lombok.AllArgsConstructor; import lombok.Getter; -import me.lucko.luckperms.core.LPConfiguration; import java.util.Collections; import java.util.Map; @@ -32,26 +31,11 @@ import java.util.Map; @Getter @AllArgsConstructor public class Contexts { - public static Contexts fromConfig(LPConfiguration configuration) { - return new Contexts( - configuration.getServer(), - null, - Collections.emptyMap(), - configuration.isIncludingGlobalPerms(), - configuration.isIncludingGlobalWorldPerms(), - true, - configuration.isApplyingGlobalGroups(), - configuration.isApplyingGlobalWorldGroups() - ); - } - public static Contexts allowAll() { - return new Contexts(null, null, Collections.emptyMap(), true, true, true, true, true); + return new Contexts(Collections.emptyMap(), true, true, true, true, true); } - private final String server; - private final String world; - private final Map extraContext; + private final Map context; private final boolean includeGlobal; private final boolean includeGlobalWorld; private final boolean applyGroups; diff --git a/common/src/main/java/me/lucko/luckperms/contexts/ServerCalculator.java b/common/src/main/java/me/lucko/luckperms/contexts/ServerCalculator.java new file mode 100644 index 000000000..b4f1ef391 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/contexts/ServerCalculator.java @@ -0,0 +1,44 @@ +/* + * 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.contexts; + +import lombok.AllArgsConstructor; +import me.lucko.luckperms.api.context.ContextCalculator; + +import java.util.Map; + +@AllArgsConstructor +public class ServerCalculator extends ContextCalculator { + private final String server; + + @Override + public Map giveApplicableContext(T subject, Map accumulator) { + accumulator.put("server", server); + return accumulator; + } + + @Override + public boolean isContextApplicable(T subject, Map.Entry context) { + return context.getKey().equals("server") && server.equals(context.getValue()); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/utils/Node.java b/common/src/main/java/me/lucko/luckperms/core/Node.java similarity index 93% rename from common/src/main/java/me/lucko/luckperms/utils/Node.java rename to common/src/main/java/me/lucko/luckperms/core/Node.java index 4ffa8bedf..5c8474807 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/Node.java +++ b/common/src/main/java/me/lucko/luckperms/core/Node.java @@ -20,13 +20,14 @@ * SOFTWARE. */ -package me.lucko.luckperms.utils; +package me.lucko.luckperms.core; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; import lombok.*; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.constants.Patterns; +import me.lucko.luckperms.utils.ArgumentChecker; import java.util.*; import java.util.regex.Pattern; @@ -100,7 +101,7 @@ public class Node implements me.lucko.luckperms.api.Node { private long expireAt = 0L; - private final Map extraContexts = new HashMap<>(); + private final Map extraContexts; // Cache the state private Tristate isPrefix = Tristate.UNDEFINED; @@ -140,9 +141,11 @@ public class Node implements me.lucko.luckperms.api.Node { this.server = server; this.world = world; + ImmutableMap.Builder contexts = ImmutableMap.builder(); if (extraContexts != null) { - this.extraContexts.putAll(extraContexts); + contexts.putAll(extraContexts); } + this.extraContexts = contexts.build(); } @Override @@ -225,36 +228,65 @@ public class Node implements me.lucko.luckperms.api.Node { } @Override - public boolean shouldApplyWithContext(Map context) { - if (context == null || context.isEmpty()) { + public boolean shouldApplyWithContext(Map context, boolean worldAndServer) { + if (extraContexts.isEmpty() && !isServerSpecific() && !isWorldSpecific()) { return true; } - for (Map.Entry c : context.entrySet()) { - if (c.getKey().equals("server")) { - if (shouldApplyOnServer(c.getValue(), false, false)) { + if (worldAndServer) { + if (isWorldSpecific()) { + if (context == null) { + return false; + } + + if (!context.containsKey("world")) { + return false; + } + + if (!context.get("world").equalsIgnoreCase(world)) { return false; } } - if (c.getKey().equals("world")) { - if (shouldApplyOnWorld(c.getValue(), false, false)) { + if (isServerSpecific()) { + if (context == null) { + return false; + } + + if (!context.containsKey("server")) { + return false; + } + + if (!context.get("server").equalsIgnoreCase(server)) { return false; } } + } - if (!getExtraContexts().containsKey(c.getKey())) { + if (!extraContexts.isEmpty()) { + if (context == null) { return false; } - if (!getExtraContexts().get(c.getKey()).equalsIgnoreCase(c.getValue())) { - return false; + for (Map.Entry c : extraContexts.entrySet()) { + if (!context.containsKey(c.getKey())) { + return false; + } + + if (!context.get(c.getKey()).equalsIgnoreCase(c.getValue())) { + return false; + } } } return true; } + @Override + public boolean shouldApplyWithContext(Map context) { + return shouldApplyWithContext(context, true); + } + @Override public boolean shouldApplyOnAnyServers(List servers, boolean includeGlobal) { for (String s : servers) { diff --git a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java index 052519c50..d9f388e90 100644 --- a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java @@ -33,10 +33,10 @@ import me.lucko.luckperms.api.event.events.PermissionNodeExpireEvent; import me.lucko.luckperms.api.event.events.PermissionNodeSetEvent; import me.lucko.luckperms.api.event.events.PermissionNodeUnsetEvent; import me.lucko.luckperms.api.implementation.internal.PermissionHolderLink; +import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectLacksException; import me.lucko.luckperms.groups.Group; -import me.lucko.luckperms.utils.Contexts; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -157,16 +157,27 @@ public abstract class PermissionHolder { .filter(Node::isGroupNode) .collect(Collectors.toSet()); + Map contexts = new HashMap<>(context.getContext()); + String server = contexts.get("server"); + String world = contexts.get("world"); + contexts.remove("server"); + contexts.remove("world"); + Iterator iterator = parents.iterator(); while (iterator.hasNext()) { Node node = iterator.next(); - if (!node.shouldApplyOnServer(context.getServer(), context.isApplyGlobalGroups(), plugin.getConfiguration().isApplyingRegex())) { + if (!node.shouldApplyOnServer(server, context.isApplyGlobalGroups(), plugin.getConfiguration().isApplyingRegex())) { iterator.remove(); continue; } - if (!node.shouldApplyOnWorld(context.getWorld(), context.isApplyGlobalWorldGroups(), plugin.getConfiguration().isApplyingRegex())) { + if (!node.shouldApplyOnWorld(world, context.isApplyGlobalWorldGroups(), plugin.getConfiguration().isApplyingRegex())) { + iterator.remove(); + continue; + } + + if (!node.shouldApplyWithContext(contexts, false)) { iterator.remove(); continue; } @@ -212,17 +223,23 @@ public abstract class PermissionHolder { allNodes = getPermissions(true); } + Map contexts = new HashMap<>(context.getContext()); + String server = contexts.get("server"); + String world = contexts.get("world"); + contexts.remove("server"); + contexts.remove("world"); + all: for (Node node : allNodes) { - if (!node.shouldApplyOnServer(context.getServer(), context.isIncludeGlobal(), plugin.getConfiguration().isApplyingRegex())) { + if (!node.shouldApplyOnServer(server, context.isIncludeGlobal(), plugin.getConfiguration().isApplyingRegex())) { continue; } - if (!node.shouldApplyOnWorld(context.getWorld(), context.isIncludeGlobalWorld(), plugin.getConfiguration().isApplyingRegex())) { + if (!node.shouldApplyOnWorld(world, context.isIncludeGlobalWorld(), plugin.getConfiguration().isApplyingRegex())) { continue; } - if (!node.shouldApplyWithContext(context.getExtraContext())) { + if (!node.shouldApplyWithContext(contexts, false)) { continue; } @@ -299,7 +316,7 @@ public abstract class PermissionHolder { // Convenience method private static Node.Builder buildNode(String permission) { - return new me.lucko.luckperms.utils.Node.Builder(permission); + return new me.lucko.luckperms.core.Node.Builder(permission); } @Deprecated @@ -307,7 +324,7 @@ public abstract class PermissionHolder { this.nodes.clear(); this.nodes.addAll(nodes.entrySet().stream() - .map(e -> me.lucko.luckperms.utils.Node.fromSerialisedNode(e.getKey(), e.getValue())) + .map(e -> me.lucko.luckperms.core.Node.fromSerialisedNode(e.getKey(), e.getValue())) .collect(Collectors.toList())); auditTemporaryPermissions(); @@ -552,12 +569,26 @@ public abstract class PermissionHolder { */ @Deprecated public Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes) { - return exportNodes(new Contexts(server, world, Collections.emptyMap(), plugin.getConfiguration().isIncludingGlobalPerms(), true, true, true, true), Collections.emptyList()); + Map context = new HashMap<>(); + if (server != null && !server.equals("")) { + context.put("server", server); + } + if (world != null && !world.equals("")) { + context.put("world", world); + } + return exportNodes(new Contexts(context, plugin.getConfiguration().isIncludingGlobalPerms(), true, true, true, true), Collections.emptyList()); } @Deprecated public Map getLocalPermissions(String server, String world, List excludedGroups) { - return exportNodes(new Contexts(server, world, Collections.emptyMap(), plugin.getConfiguration().isIncludingGlobalPerms(), true, true, true, true), Collections.emptyList()); + Map context = new HashMap<>(); + if (server != null && !server.equals("")) { + context.put("server", server); + } + if (world != null && !world.equals("")) { + context.put("world", world); + } + return exportNodes(new Contexts(context, plugin.getConfiguration().isIncludingGlobalPerms(), true, true, true, true), Collections.emptyList()); } @SuppressWarnings("deprecation") diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java index 36cf28e90..1eb5fab6f 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java @@ -26,10 +26,10 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import lombok.Cleanup; import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.core.Node; import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.users.User; -import me.lucko.luckperms.utils.Node; import java.io.*; import java.util.*; diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java index bc039e57a..ae4d1e89f 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java @@ -24,10 +24,10 @@ package me.lucko.luckperms.storage.methods; import lombok.Cleanup; import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.core.Node; import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.users.User; -import me.lucko.luckperms.utils.Node; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; diff --git a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java index a14c66246..c450914ed 100644 --- a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java @@ -30,9 +30,13 @@ import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.api.sponge.LuckPermsService; 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.core.LPConfiguration; +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; import me.lucko.luckperms.data.Importer; import me.lucko.luckperms.groups.GroupManager; @@ -42,7 +46,6 @@ 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; @@ -86,7 +89,7 @@ public class LPSpongePlugin implements LuckPermsPlugin { private final Set ignoringLogs = ConcurrentHashMap.newKeySet(); private LPConfiguration configuration; - private UserManager userManager; + private SpongeUserManager userManager; private GroupManager groupManager; private TrackManager trackManager; private Datastore datastore; @@ -97,6 +100,7 @@ public class LPSpongePlugin implements LuckPermsPlugin { private ConsecutiveExecutor consecutiveExecutor; private LuckPermsService service; private LocaleManager localeManager; + private ContextManager contextManager; @Listener public void onEnable(GamePreInitializationEvent event) { @@ -135,6 +139,11 @@ public class LPSpongePlugin implements LuckPermsPlugin { importer = new Importer(commandManager); consecutiveExecutor = new ConsecutiveExecutor(commandManager); + 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))); @@ -155,6 +164,7 @@ 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."); } diff --git a/sponge/src/main/java/me/lucko/luckperms/SpongeConfig.java b/sponge/src/main/java/me/lucko/luckperms/SpongeConfig.java index 8dd912d2d..e45a45d22 100644 --- a/sponge/src/main/java/me/lucko/luckperms/SpongeConfig.java +++ b/sponge/src/main/java/me/lucko/luckperms/SpongeConfig.java @@ -23,7 +23,7 @@ package me.lucko.luckperms; import com.google.common.base.Splitter; -import me.lucko.luckperms.core.AbstractConfiguration; +import me.lucko.luckperms.config.AbstractConfiguration; import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.commented.CommentedConfigurationNode; import ninja.leaping.configurate.hocon.HoconConfigurationLoader; diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/ContextData.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/ContextData.java new file mode 100644 index 000000000..5eb8385c4 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/ContextData.java @@ -0,0 +1,78 @@ +/* + * 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.sponge; + +import lombok.Getter; +import lombok.NonNull; +import me.lucko.luckperms.api.Tristate; +import me.lucko.luckperms.calculators.PermissionCalculator; +import me.lucko.luckperms.calculators.PermissionProcessor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ContextData { + private final LuckPermsUserSubject parent; + + @Getter + private final Map context; + + @Getter + private final Map permissionCache = new ConcurrentHashMap<>(); + + private final PermissionCalculator calculator; + + public ContextData(LuckPermsUserSubject parent, Map context, LuckPermsService service) { + this.parent = parent; + this.context = context; + + List processors = new ArrayList<>(5); + processors.add(new PermissionCalculator.MapProcessor(permissionCache)); + if (service.getPlugin().getConfiguration().isApplyingWildcards()) { + processors.add(new LuckPermsUserSubject.SpongeWildcardProcessor(permissionCache)); + processors.add(new PermissionCalculator.WildcardProcessor(permissionCache)); + } + if (service.getPlugin().getConfiguration().isApplyingRegex()) { + processors.add(new PermissionCalculator.RegexProcessor(permissionCache)); + } + processors.add(new LuckPermsUserSubject.SpongeDefaultsProcessor(service)); + + calculator = new PermissionCalculator(service.getPlugin(), parent.getUser().getName(), service.getPlugin().getConfiguration().isDebugPermissionChecks(), processors); + } + + public void invalidateCache() { + calculator.invalidateCache(); + } + + public Tristate getPermissionValue(@NonNull String permission) { + me.lucko.luckperms.api.Tristate t = calculator.getPermissionValue(permission); + if (t != me.lucko.luckperms.api.Tristate.UNDEFINED) { + return Tristate.fromBoolean(t.asBoolean()); + } else { + return Tristate.UNDEFINED; + } + } + +} 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 8cbccfc65..a0a65f34f 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 @@ -29,6 +29,7 @@ import me.lucko.luckperms.LPSpongePlugin; import me.lucko.luckperms.api.sponge.collections.GroupCollection; import me.lucko.luckperms.api.sponge.collections.UserCollection; import me.lucko.luckperms.api.sponge.simple.SimpleCollection; +import me.lucko.luckperms.contexts.SpongeCalculatorLink; import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.api.service.context.ContextCalculator; import org.spongepowered.api.service.permission.*; @@ -54,7 +55,6 @@ public class LuckPermsService implements PermissionService { private final Set descriptionSet; private final Map subjects; - private final Set> contextCalculators; // TODO actually use context calculators, idk... public LuckPermsService(LPSpongePlugin plugin) { this.plugin = plugin; @@ -67,7 +67,6 @@ public class LuckPermsService implements PermissionService { subjects.put(PermissionService.SUBJECTS_GROUP, groupSubjects); descriptionSet = ConcurrentHashMap.newKeySet(); - contextCalculators = ConcurrentHashMap.newKeySet(); } public SubjectData getDefaultData() { @@ -121,7 +120,7 @@ public class LuckPermsService implements PermissionService { @Override public void registerContextCalculator(@NonNull ContextCalculator contextCalculator) { - contextCalculators.add(contextCalculator); + plugin.getContextManager().registerCalculator(new SpongeCalculatorLink(contextCalculator)); } @RequiredArgsConstructor diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java index 9afd3b7de..506065fb4 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/LuckPermsSubject.java @@ -30,12 +30,12 @@ import lombok.Getter; import lombok.NonNull; import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.data.Callback; +import me.lucko.luckperms.contexts.Contexts; 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 me.lucko.luckperms.utils.Contexts; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.permission.Subject; @@ -124,7 +124,7 @@ public class LuckPermsSubject implements Subject { context.put(c.getKey(), c.getValue()); } - switch (holder.inheritsPermission(new me.lucko.luckperms.utils.Node.Builder(node).withExtraContext(context).build())) { + switch (holder.inheritsPermission(new me.lucko.luckperms.core.Node.Builder(node).withExtraContext(context).build())) { case UNDEFINED: return Tristate.UNDEFINED; case TRUE: @@ -277,7 +277,7 @@ public class LuckPermsSubject implements Subject { public boolean setPermission(Set set, String s, Tristate tristate) { if (tristate == Tristate.UNDEFINED) { // Unset - Node.Builder builder = new me.lucko.luckperms.utils.Node.Builder(s); + Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s); for (Context ct : set) { builder.withExtraContext(ct.getKey(), ct.getValue()); @@ -290,7 +290,7 @@ public class LuckPermsSubject implements Subject { return true; } - Node.Builder builder = new me.lucko.luckperms.utils.Node.Builder(s) + Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s) .setValue(tristate.asBoolean()); for (Context ct : set) { @@ -384,7 +384,7 @@ public class LuckPermsSubject implements Subject { Map contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); try { - holder.setPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier()) + holder.setPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier()) .withExtraContext(contexts) .build()); } catch (ObjectAlreadyHasException ignored) {} @@ -403,7 +403,7 @@ public class LuckPermsSubject implements Subject { Map contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); try { - holder.unsetPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier()) + holder.unsetPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier()) .withExtraContext(contexts) .build()); } catch (ObjectLacksException ignored) {} @@ -514,7 +514,7 @@ public class LuckPermsSubject implements Subject { value = escapeCharacters(value); try { - holder.setPermission(new me.lucko.luckperms.utils.Node.Builder("meta." + key + "." + value) + holder.setPermission(new me.lucko.luckperms.core.Node.Builder("meta." + key + "." + value) .withExtraContext(context) .build() ); @@ -613,7 +613,7 @@ public class LuckPermsSubject implements Subject { if (tristate == Tristate.UNDEFINED) { // Unset - Node.Builder builder = new me.lucko.luckperms.utils.Node.Builder(s); + Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s); for (Context ct : set) { builder.withExtraContext(ct.getKey(), ct.getValue()); @@ -625,7 +625,7 @@ public class LuckPermsSubject implements Subject { return true; } - Node.Builder builder = new me.lucko.luckperms.utils.Node.Builder(s) + Node.Builder builder = new me.lucko.luckperms.core.Node.Builder(s) .setValue(tristate.asBoolean()); for (Context ct : set) { @@ -709,7 +709,7 @@ public class LuckPermsSubject implements Subject { Map contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); try { - holder.setTransientPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier()) + holder.setTransientPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier()) .withExtraContext(contexts) .build()); } catch (ObjectAlreadyHasException ignored) {} @@ -727,7 +727,7 @@ public class LuckPermsSubject implements Subject { Map contexts = set.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); try { - holder.unsetTransientPermission(new me.lucko.luckperms.utils.Node.Builder("group." + permsSubject.getIdentifier()) + holder.unsetTransientPermission(new me.lucko.luckperms.core.Node.Builder("group." + permsSubject.getIdentifier()) .withExtraContext(contexts) .build()); } catch (ObjectLacksException ignored) {} @@ -827,7 +827,7 @@ public class LuckPermsSubject implements Subject { value = escapeCharacters(value); try { - holder.setTransientPermission(new me.lucko.luckperms.utils.Node.Builder("meta." + key + "." + value) + holder.setTransientPermission(new me.lucko.luckperms.core.Node.Builder("meta." + key + "." + value) .withExtraContext(context) .build() ); 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 7685ae6e0..6e4fbd24c 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 @@ -25,17 +25,21 @@ package me.lucko.luckperms.api.sponge; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NonNull; +import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent; +import me.lucko.luckperms.api.implementation.internal.UserLink; +import me.lucko.luckperms.calculators.PermissionProcessor; +import me.lucko.luckperms.contexts.Contexts; import me.lucko.luckperms.users.User; -import me.lucko.luckperms.utils.PermissionCalculator; -import me.lucko.luckperms.utils.PermissionProcessor; 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.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 { public static LuckPermsUserSubject wrapUser(User user, LuckPermsService service) { @@ -45,37 +49,22 @@ public class LuckPermsUserSubject extends LuckPermsSubject { @Getter private final User user; - private final PermissionCalculator calculator; - @Getter - private final Map permissionCache = new ConcurrentHashMap<>(); + private final Map, ContextData> contextData; private LuckPermsUserSubject(User user, LuckPermsService service) { super(user, service); this.user = user; - List processors = new ArrayList<>(5); - processors.add(new PermissionCalculator.MapProcessor(permissionCache)); - if (service.getPlugin().getConfiguration().isApplyingWildcards()) { - processors.add(new SpongeWildcardProcessor(permissionCache)); - processors.add(new PermissionCalculator.WildcardProcessor(permissionCache)); - } - if (service.getPlugin().getConfiguration().isApplyingRegex()) { - processors.add(new PermissionCalculator.RegexProcessor(permissionCache)); - } - processors.add(new SpongeDefaultsProcessor(service)); - - calculator = new PermissionCalculator(service.getPlugin(), user.getName(), service.getPlugin().getConfiguration().isDebugPermissionChecks(), processors); + contextData = new ConcurrentHashMap<>(); } - public void invalidateCache() { - calculator.invalidateCache(); - } - - // TODO don't ignore context @Override public Tristate getPermissionValue(@NonNull Set contexts, @NonNull String permission) { - me.lucko.luckperms.api.Tristate t = calculator.getPermissionValue(permission); + Map context = contexts.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); + ContextData cd = contextData.computeIfAbsent(context, this::calculatePermissions); + + me.lucko.luckperms.api.Tristate t = cd.getPermissionValue(permission); if (t != me.lucko.luckperms.api.Tristate.UNDEFINED) { return Tristate.fromBoolean(t.asBoolean()); } else { @@ -83,6 +72,57 @@ public class LuckPermsUserSubject extends LuckPermsSubject { } } + public ContextData calculatePermissions(Map context) { + 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() + ); + + ContextData existing = contextData.get(context); + if (existing == null) { + existing = new ContextData(this, context, service); + 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) { + Map context = contexts.stream().collect(Collectors.toMap(Context::getKey, Context::getValue)); + calculatePermissions(context); + } + + public void calculateActivePermissions() { + calculatePermissions(getActiveContexts()); + } + + @Override public String getIdentifier() { return service.getPlugin().getUuidCache().getExternalUUID(user.getUuid()).toString(); @@ -100,8 +140,22 @@ public class LuckPermsUserSubject extends LuckPermsSubject { return Optional.empty(); } + @Override + public Set getActiveContexts() { + final UUID uuid = service.getPlugin().getUuidCache().getExternalUUID(user.getUuid()); + Optional player = Sponge.getServer().getPlayer(uuid); + + if (!player.isPresent()) { + return SubjectData.GLOBAL_CONTEXT; + } + + Map context = new HashMap<>(); + service.getPlugin().getContextManager().giveApplicableContext(player.get(), context); + return context.entrySet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet()); + } + @AllArgsConstructor - private static class SpongeWildcardProcessor implements PermissionProcessor { + static class SpongeWildcardProcessor implements PermissionProcessor { @Getter private final Map map; @@ -152,7 +206,7 @@ public class LuckPermsUserSubject extends LuckPermsSubject { } @AllArgsConstructor - private static class SpongeDefaultsProcessor implements PermissionProcessor { + static class SpongeDefaultsProcessor implements PermissionProcessor { private final LuckPermsService service; @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/contexts/ContextUpdateTask.java b/sponge/src/main/java/me/lucko/luckperms/contexts/ContextUpdateTask.java new file mode 100644 index 000000000..1b7638542 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/contexts/ContextUpdateTask.java @@ -0,0 +1,45 @@ +/* + * 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.contexts; + +import lombok.AllArgsConstructor; +import me.lucko.luckperms.api.sponge.LuckPermsUserSubject; +import me.lucko.luckperms.api.sponge.collections.UserCollection; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@AllArgsConstructor +public class ContextUpdateTask implements Runnable { + private final UserCollection userCollection; + + @Override + public void run() { + for (LuckPermsUserSubject subject : userCollection.getUsers().values()) { + Set> contexts = new HashSet<>(subject.getContextData().keySet()); + contexts.forEach(subject::calculatePermissions); + } + } + +} diff --git a/sponge/src/main/java/me/lucko/luckperms/contexts/SpongeCalculatorLink.java b/sponge/src/main/java/me/lucko/luckperms/contexts/SpongeCalculatorLink.java new file mode 100644 index 000000000..b1ec5c842 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/contexts/SpongeCalculatorLink.java @@ -0,0 +1,53 @@ +/* + * 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.contexts; + +import lombok.AllArgsConstructor; +import me.lucko.luckperms.api.context.ContextCalculator; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.Subject; + +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class SpongeCalculatorLink extends ContextCalculator { + private final org.spongepowered.api.service.context.ContextCalculator calculator; + + @Override + public Map giveApplicableContext(Player subject, Map accumulator) { + Set contexts = accumulator.entrySet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet()); + calculator.accumulateContexts(subject, contexts); + + contexts.forEach(c -> accumulator.put(c.getKey(), c.getValue())); + return accumulator; + } + + @Override + public boolean isContextApplicable(Player subject, Map.Entry context) { + Context c = new Context(context.getKey(), context.getValue()); + return calculator.matches(c, subject); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/contexts/WorldCalculator.java b/sponge/src/main/java/me/lucko/luckperms/contexts/WorldCalculator.java new file mode 100644 index 000000000..e8ed9682d --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/contexts/WorldCalculator.java @@ -0,0 +1,46 @@ +/* + * 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.contexts; + +import lombok.RequiredArgsConstructor; +import me.lucko.luckperms.api.context.ContextCalculator; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.service.context.Context; + +import java.util.Map; + +@RequiredArgsConstructor +public class WorldCalculator extends ContextCalculator { + + @Override + public Map giveApplicableContext(Player subject, Map accumulator) { + accumulator.put(Context.WORLD_KEY, subject.getWorld().getName()); + return accumulator; + } + + @Override + public boolean isContextApplicable(Player subject, Map.Entry context) { + return context.getKey().equals(Context.WORLD_KEY) && subject.getWorld().getName().equals(context.getValue()); + } + +} diff --git a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java index 907ae7453..37b3610cf 100644 --- a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java +++ b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java @@ -23,14 +23,9 @@ package me.lucko.luckperms.users; import me.lucko.luckperms.LPSpongePlugin; -import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent; -import me.lucko.luckperms.api.implementation.internal.UserLink; import me.lucko.luckperms.api.sponge.LuckPermsUserSubject; import me.lucko.luckperms.api.sponge.collections.UserCollection; -import me.lucko.luckperms.utils.Contexts; -import java.util.Collections; -import java.util.Map; import java.util.UUID; class SpongeUser extends User { @@ -53,47 +48,7 @@ class SpongeUser extends User { 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.getConfiguration().getServer(), - null, // TODO per world perms - null, - plugin.getConfiguration().isIncludingGlobalPerms(), - plugin.getConfiguration().isIncludingGlobalWorldPerms(), - true, - plugin.getConfiguration().isApplyingGlobalGroups(), - plugin.getConfiguration().isApplyingGlobalWorldGroups() - ), - Collections.emptyList() - ); - - try { - LuckPermsUserSubject us = uc.getUsers().get(getUuid()); - Map existing = us.getPermissionCache(); - - 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(); - us.invalidateCache(); - existing.putAll(toApply); - - plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this))); - } catch (Exception e) { - e.printStackTrace(); - } + LuckPermsUserSubject us = uc.getUsers().get(getUuid()); + us.calculateActivePermissions(); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java index 231661d15..a580964c3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java @@ -23,10 +23,13 @@ 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; import java.util.UUID; -public class SpongeUserManager extends UserManager { +public class SpongeUserManager extends UserManager implements ContextListener { private final LPSpongePlugin plugin; public SpongeUserManager(LPSpongePlugin plugin) { @@ -58,4 +61,14 @@ public class SpongeUserManager extends UserManager { .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 { + UUID internal = plugin.getUuidCache().getUUID(subject.getUniqueId()); + + User user = get(internal); + if (user != null) { + plugin.doAsync(user::refreshPermissions); + } + } } diff --git a/standalone/src/main/java/me/lucko/luckperms/internal/StandaloneBase.java b/standalone/src/main/java/me/lucko/luckperms/internal/StandaloneBase.java index 22cebadb5..2341e2ea5 100644 --- a/standalone/src/main/java/me/lucko/luckperms/internal/StandaloneBase.java +++ b/standalone/src/main/java/me/lucko/luckperms/internal/StandaloneBase.java @@ -30,10 +30,11 @@ import me.lucko.luckperms.api.PlatformType; import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.commands.ConsecutiveExecutor; import me.lucko.luckperms.commands.Sender; +import me.lucko.luckperms.config.LPConfiguration; import me.lucko.luckperms.constants.Constants; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Permission; -import me.lucko.luckperms.core.LPConfiguration; +import me.lucko.luckperms.contexts.ContextManager; import me.lucko.luckperms.core.UuidCache; import me.lucko.luckperms.data.Importer; import me.lucko.luckperms.groups.GroupManager; @@ -146,6 +147,11 @@ public class StandaloneBase implements LuckPermsPlugin { return null; } + @Override + public ContextManager getContextManager() { + return null; + } + @Override public Message getPlayerStatus(UUID uuid) { return Message.PLAYER_OFFLINE; diff --git a/standalone/src/main/java/me/lucko/luckperms/internal/StandaloneConfiguration.java b/standalone/src/main/java/me/lucko/luckperms/internal/StandaloneConfiguration.java index 7d7605c76..ce7600446 100644 --- a/standalone/src/main/java/me/lucko/luckperms/internal/StandaloneConfiguration.java +++ b/standalone/src/main/java/me/lucko/luckperms/internal/StandaloneConfiguration.java @@ -22,7 +22,7 @@ package me.lucko.luckperms.internal; -import me.lucko.luckperms.core.AbstractConfiguration; +import me.lucko.luckperms.config.AbstractConfiguration; import java.util.Map;