diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java index 701de654..9b328618 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java @@ -173,7 +173,7 @@ public class BukkitListener implements Listener { t.printStackTrace(); } - plugin.refreshAutoOp(player); + plugin.refreshAutoOp(user, player); } @EventHandler(priority = EventPriority.MONITOR) @@ -201,7 +201,7 @@ public class BukkitListener implements Listener { } // everything is going well. login was processed ok, this is just to refresh auto-op status. - plugin.refreshAutoOp(e.getPlayer()); + plugin.refreshAutoOp(plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()), e.getPlayer()); } // Wait until the last priority to unload, so plugins can still perform permission checks on this event @@ -252,6 +252,6 @@ public class BukkitListener implements Listener { @EventHandler(priority = EventPriority.LOWEST) public void onWorldChange(PlayerChangedWorldEvent e) { plugin.getContextManager().invalidateCache(e.getPlayer()); - plugin.refreshAutoOp(e.getPlayer()); + plugin.refreshAutoOp(plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()), e.getPlayer()); } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java index 3e95aeb4..f4928726 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java @@ -402,23 +402,15 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { } } - public void refreshAutoOp(Player player) { + public void refreshAutoOp(User user, Player player) { + if (user == null) { + return; + } + if (getConfiguration().get(ConfigKeys.AUTO_OP)) { - try { - LPPermissible permissible = Injector.getPermissible(player.getUniqueId()); - if (permissible == null || !permissible.getActive().get()) { - return; - } - - User user = permissible.getUser(); - if (user == null) { - return; - } - - Map backing = user.getUserData().getPermissionData(permissible.calculateContexts()).getImmutableBacking(); - boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false); - player.setOp(op); - } catch (Exception ignored) {} + Map backing = user.getUserData().getPermissionData(contextManager.getApplicableContexts(player)).getImmutableBacking(); + boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false); + player.setOp(op); } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java index 6fbb4622..05b6c9db 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java @@ -31,9 +31,6 @@ import com.google.common.collect.ImmutableList; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.bukkit.LPBukkitPlugin; -import me.lucko.luckperms.bukkit.model.Injector; -import me.lucko.luckperms.bukkit.model.LPPermissible; -import me.lucko.luckperms.bukkit.processors.AttachmentProcessor; import me.lucko.luckperms.bukkit.processors.ChildProcessor; import me.lucko.luckperms.bukkit.processors.DefaultsProcessor; import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; @@ -47,7 +44,6 @@ import me.lucko.luckperms.common.processors.RegexProcessor; import me.lucko.luckperms.common.processors.WildcardProcessor; import java.util.List; -import java.util.UUID; @AllArgsConstructor public class BukkitCalculatorFactory extends AbstractCalculatorFactory { @@ -63,14 +59,6 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory { processors.add(new ChildProcessor(plugin.getChildPermissionProvider())); } - if (plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) { - final UUID uuid = plugin.getUuidCache().getExternalUUID(user.getUuid()); - processors.add(new AttachmentProcessor(() -> { - LPPermissible permissible = Injector.getPermissible(uuid); - return permissible == null ? null : permissible.getAttachmentPermissions(); - })); - } - if (plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) { processors.add(new RegexProcessor()); } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPermissible.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPermissible.java index f01578c8..9b03efe7 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPermissible.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPermissible.java @@ -45,63 +45,17 @@ public class DummyPermissible implements Permissible { onRefresh.run(); } - @Override - public Set getEffectivePermissions() { - return Collections.emptySet(); - } + @Override public Set getEffectivePermissions() { return Collections.emptySet(); } + @Override public boolean isPermissionSet(String name) { return false; } + @Override public boolean isPermissionSet(Permission perm) { return false; } + @Override public boolean hasPermission(String name) { return false; } + @Override public boolean hasPermission(Permission perm) { return false; } + @Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { return null; } + @Override public PermissionAttachment addAttachment(Plugin plugin) { return null; } + @Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { return null; } + @Override public PermissionAttachment addAttachment(Plugin plugin, int ticks) { return null; } + @Override public void removeAttachment(PermissionAttachment attachment) {} + @Override public boolean isOp() { return false; } + @Override public void setOp(boolean value) {} - @Override - public boolean isPermissionSet(String name) { - return false; - } - - @Override - public boolean isPermissionSet(Permission perm) { - return false; - } - - @Override - public boolean hasPermission(String name) { - return false; - } - - @Override - public boolean hasPermission(Permission perm) { - return false; - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { - return null; - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin) { - return null; - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { - return null; - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, int ticks) { - return null; - } - - @Override - public void removeAttachment(PermissionAttachment attachment) { - - } - - @Override - public boolean isOp() { - return false; - } - - @Override - public void setOp(boolean value) { - - } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPermissibleBase.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPermissibleBase.java index 609d0e25..2a5b6b56 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPermissibleBase.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPermissibleBase.java @@ -35,77 +35,25 @@ import java.util.Collections; import java.util.Set; public class DummyPermissibleBase extends PermissibleBase { - public DummyPermissibleBase() { + public static final DummyPermissibleBase INSTANCE = new DummyPermissibleBase(); + + private DummyPermissibleBase() { super(null); } - @Override - public boolean isOp() { - return false; - } + @Override public boolean isOp() { return false; } + @Override public void setOp(boolean value) {} + @Override public boolean isPermissionSet(String name) { return false; } + @Override public boolean isPermissionSet(Permission perm) { return false; } + @Override public boolean hasPermission(String inName) { return false; } + @Override public boolean hasPermission(Permission perm) { return false; } + @Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { return null; } + @Override public PermissionAttachment addAttachment(Plugin plugin) { return null; } + @Override public void removeAttachment(PermissionAttachment attachment) {} + @Override public void recalculatePermissions() {} + @Override public void clearPermissions() {} + @Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { return null; } + @Override public PermissionAttachment addAttachment(Plugin plugin, int ticks) { return null; } + @Override public Set getEffectivePermissions() { return Collections.emptySet(); } - @Override - public void setOp(boolean value) { - - } - - @Override - public boolean isPermissionSet(String name) { - return false; - } - - @Override - public boolean isPermissionSet(Permission perm) { - return false; - } - - @Override - public boolean hasPermission(String inName) { - return false; - } - - @Override - public boolean hasPermission(Permission perm) { - return false; - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { - return null; - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin) { - return null; - } - - @Override - public void removeAttachment(PermissionAttachment attachment) { - - } - - @Override - public void recalculatePermissions() { - - } - - @Override - public synchronized void clearPermissions() { - - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { - return null; - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, int ticks) { - return null; - } - - @Override - public Set getEffectivePermissions() { - return Collections.emptySet(); - } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPlugin.java new file mode 100644 index 00000000..9387e249 --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/DummyPlugin.java @@ -0,0 +1,78 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.bukkit.model; + +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginLoader; + +import java.io.File; +import java.io.InputStream; +import java.util.List; +import java.util.logging.Logger; + +/** + * Dummy plugin instance + */ +public class DummyPlugin implements Plugin { + public static final DummyPlugin INSTANCE = new DummyPlugin(); + + private DummyPlugin() { + + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override public File getDataFolder() { return null; } + @Override public PluginDescriptionFile getDescription() { return null; } + @Override public FileConfiguration getConfig() { return null; } + @Override public InputStream getResource(String s) { return null; } + @Override public void saveConfig() {} + @Override public void saveDefaultConfig() {} + @Override public void saveResource(String s, boolean b) {} + @Override public void reloadConfig() {} + @Override public PluginLoader getPluginLoader() { return null; } + @Override public Server getServer() { return null; } + @Override public void onDisable() {} + @Override public void onLoad() {} + @Override public void onEnable() {} + @Override public boolean isNaggable() { return false; } + @Override public void setNaggable(boolean b) {} + @Override public ChunkGenerator getDefaultWorldGenerator(String s, String s1) { return null; } + @Override public Logger getLogger() { return null; } + @Override public String getName() { return null; } + @Override public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings) { return false; } + @Override public List onTabComplete(CommandSender commandSender, Command command, String s, String[] strings) { return null; } + +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/Injector.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/Injector.java index dc998184..ff4da499 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/Injector.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/Injector.java @@ -116,13 +116,12 @@ public class Injector { //noinspection unchecked List attachments = (List) PERMISSIBLE_BASE_ATTACHMENTS_FIELD.get(oldPermissible); - newPermissible.addAttachments(attachments); + newPermissible.convertAndAddAttachments(attachments); attachments.clear(); oldPermissible.clearPermissions(); // Setup the new permissible newPermissible.getActive().set(true); - newPermissible.recalculatePermissions(false); newPermissible.setOldPermissible(oldPermissible); newPermissible.updateSubscriptionsAsync(); @@ -164,23 +163,14 @@ public class Injector { // handle the replacement permissible. if (dummy) { // just inject a dummy class. this is used when we know the player is about to quit the server. - HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, new DummyPermissibleBase()); + HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, DummyPermissibleBase.INSTANCE); } else { - // otherwise, inject the permissible they had when we first injected. - - List lpAttachments = lpPermissible.getAttachments(); - PermissibleBase newPb = lpPermissible.getOldPermissible(); if (newPb == null) { newPb = new PermissibleBase(player); } - //noinspection unchecked - List newPbAttachments = (List) PERMISSIBLE_BASE_ATTACHMENTS_FIELD.get(newPb); - newPbAttachments.addAll(lpAttachments); - lpAttachments.clear(); - HUMAN_ENTITY_PERMISSIBLE_FIELD.set(player, newPb); } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java index 50888151..752f209b 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java @@ -37,25 +37,19 @@ import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.verbose.CheckOrigin; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.permissions.PermissibleBase; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachmentInfo; -import org.bukkit.permissions.PermissionRemovedExecutor; import org.bukkit.plugin.Plugin; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; import java.util.stream.Collectors; /** @@ -94,12 +88,9 @@ public class LPPermissible extends PermissibleBase { // if the permissible is currently active. private final AtomicBoolean active = new AtomicBoolean(false); - // the permissions registered by PermissionAttachments. - // stored in this format, as that's what is used by #getEffectivePermissions - private final Map attachmentPermissions = new ConcurrentHashMap<>(); - // the attachments hooked onto the permissible. - private final List attachments = Collections.synchronizedList(new ArrayList<>()); + // this collection is only modified by the attachments themselves + final Set attachments = ConcurrentHashMap.newKeySet(); public LPPermissible(@NonNull Player parent, @NonNull User user, @NonNull LPBukkitPlugin plugin) { super(parent); @@ -204,8 +195,10 @@ public class LPPermissible extends PermissibleBase { * * @param attachments the attachments to add */ - public void addAttachments(Collection attachments) { - this.attachments.addAll(attachments); + public void convertAndAddAttachments(Collection attachments) { + for (PermissionAttachment attachment : attachments) { + new LPPermissionAttachment(this, attachment).hook(); + } } /** @@ -226,135 +219,70 @@ public class LPPermissible extends PermissibleBase { @Override public Set getEffectivePermissions() { Set perms = new HashSet<>(); - perms.addAll(attachmentPermissions.values()); - perms.addAll( user.getUserData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet().stream() .map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue())) .collect(Collectors.toList()) ); - return perms; } @Override - public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value) { - if (!plugin.isEnabled()) { - throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled"); - } - - PermissionAttachment result = addAttachment(plugin); - result.setPermission(name, value); - - recalculatePermissions(); - - return result; + public LPPermissionAttachment addAttachment(Plugin plugin) { + LPPermissionAttachment ret = new LPPermissionAttachment(this, plugin); + ret.hook(); + return ret; } @Override - public PermissionAttachment addAttachment(@NonNull Plugin plugin) { - if (!plugin.isEnabled()) { - throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled"); - } - - PermissionAttachment result = new PermissionAttachment(plugin, parent); - - attachments.add(result); - recalculatePermissions(); - - return result; + public PermissionAttachment addAttachment(Plugin plugin, @NonNull String name, boolean value) { + PermissionAttachment ret = addAttachment(plugin); + ret.setPermission(name, value); + return ret; } @Override - public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value, int ticks) { + public LPPermissionAttachment addAttachment(@NonNull Plugin plugin, int ticks) { if (!plugin.isEnabled()) { throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled"); } - PermissionAttachment result = addAttachment(plugin, ticks); - if (result != null) { - result.setPermission(name, value); + LPPermissionAttachment ret = addAttachment(plugin); + if (getPlugin().getServer().getScheduler().scheduleSyncDelayedTask(plugin, ret::remove, ticks) == -1) { + ret.remove(); + throw new RuntimeException("Could not add PermissionAttachment to " + parent + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1"); } - - return result; + return ret; } @Override - public PermissionAttachment addAttachment(@NonNull Plugin plugin, int ticks) { - if (!plugin.isEnabled()) { - throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled"); - } - - PermissionAttachment result = addAttachment(plugin); - if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, result::remove, ticks) == -1) { - Bukkit.getServer().getLogger().log(Level.WARNING, "Could not add PermissionAttachment to " + parent + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1"); - result.remove(); - return null; - } else { - return result; - } + public LPPermissionAttachment addAttachment(Plugin plugin, @NonNull String name, boolean value, int ticks) { + LPPermissionAttachment ret = addAttachment(plugin, ticks); + ret.setPermission(name, value); + return ret; } @Override public void removeAttachment(@NonNull PermissionAttachment attachment) { - if (attachments.contains(attachment)) { - attachments.remove(attachment); - PermissionRemovedExecutor ex = attachment.getRemovalCallback(); - - if (ex != null) { - ex.attachmentRemoved(attachment); - } - - recalculatePermissions(); - } else { - throw new IllegalArgumentException("Given attachment is not part of Permissible object " + parent); + if (!(attachment instanceof LPPermissionAttachment)) { + throw new IllegalArgumentException("Given attachment is not a LPPermissionAttachment."); } + + LPPermissionAttachment a = ((LPPermissionAttachment) attachment); + if (a.getPermissible() != this) { + throw new IllegalArgumentException("Attachment does not belong to this permissible."); + } + + a.remove(); } @Override public void recalculatePermissions() { - recalculatePermissions(true); - } - - public void recalculatePermissions(boolean invalidate) { - if (attachmentPermissions == null) { - return; - } - - attachmentPermissions.clear(); - - for (PermissionAttachment attachment : attachments) { - calculateChildPermissions(attachment.getPermissions(), false, attachment); - } - - if (invalidate) { - user.getUserData().invalidatePermissionCalculators(); - } + // do nothing } @Override - public synchronized void clearPermissions() { - Set perms = attachmentPermissions.keySet(); - - for (String name : perms) { - Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, parent); - } - - attachmentPermissions.clear(); - } - - private void calculateChildPermissions(Map children, boolean invert, PermissionAttachment attachment) { - for (Map.Entry e : children.entrySet()) { - Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey()); - boolean value = e.getValue() ^ invert; - String name = e.getKey().toLowerCase(); - - attachmentPermissions.put(name, new PermissionAttachmentInfo(parent, name, attachment, value)); - Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent); - - if (perm != null) { - calculateChildPermissions(perm.getChildren(), !value, attachment); - } - } + public void clearPermissions() { + attachments.forEach(LPPermissionAttachment::remove); } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java new file mode 100644 index 00000000..79f87e68 --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java @@ -0,0 +1,192 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.bukkit.model; + +import lombok.Getter; +import lombok.Setter; + +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.node.ImmutableTransientNode; +import me.lucko.luckperms.common.node.NodeFactory; + +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionRemovedExecutor; +import org.bukkit.plugin.Plugin; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * PermissionAttachment for LuckPerms. + * + * Applies all permissions directly to the backing user instance via transient nodes. + */ +public class LPPermissionAttachment extends PermissionAttachment { + + /** + * The parent LPPermissible + */ + @Getter + private final LPPermissible permissible; + + /** + * The plugin which "owns" this attachment, may be null + */ + private final Plugin owner; + + /** + * The permissions being applied by this attachment + */ + private final Map perms = Collections.synchronizedMap(new HashMap<>()); + + /** + * If the attachment has been applied to the user + */ + private boolean hooked = false; + + /** + * Callback to run when the attachment is removed + */ + @Getter + @Setter + private PermissionRemovedExecutor removalCallback = null; + + public LPPermissionAttachment(LPPermissible permissible, Plugin owner) { + super(DummyPlugin.INSTANCE, null); + this.permissible = permissible; + this.owner = owner; + } + + public LPPermissionAttachment(LPPermissible permissible, PermissionAttachment bukkit) { + super(DummyPlugin.INSTANCE, null); + this.permissible = permissible; + this.owner = null; + + // copy + perms.putAll(bukkit.getPermissions()); + } + + public void hook() { + hooked = true; + permissible.attachments.add(this); + for (Map.Entry entry : perms.entrySet()) { + setPermissionInternal(entry.getKey(), entry.getValue()); + } + } + + private void setPermissionInternal(String name, boolean value) { + if (!permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) { + return; + } + + ImmutableTransientNode node = ImmutableTransientNode.of(NodeFactory.make(name, value), this); + if (permissible.getUser().setTransientPermission(node).asBoolean()) { + permissible.getUser().getRefreshBuffer().request(); + } + } + + private void unsetPermissionInternal(String name) { + if (!permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) { + return; + } + + if (permissible.getUser().removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name))) { + permissible.getUser().getRefreshBuffer().request(); + } + } + + @Override + public boolean remove() { + if (!hooked) { + return false; + } + + if (permissible.getUser().removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this)) { + permissible.getUser().getRefreshBuffer().request(); + } + + if (removalCallback != null) { + removalCallback.attachmentRemoved(this); + } + + hooked = false; + permissible.attachments.remove(this); + return true; + } + + @Override + public void setPermission(String name, boolean value) { + Boolean previous = perms.put(name, value); + if (previous != null && previous == value) { + return; + } + + if (!hooked) { + return; + } + + if (previous != null) { + unsetPermissionInternal(name); + } + + setPermissionInternal(name, value); + } + + @Override + public void unsetPermission(String name) { + Boolean previous = perms.remove(name); + if (previous == null) { + return; + } + + if (!hooked) { + return; + } + + unsetPermissionInternal(name); + } + + @Override + public Map getPermissions() { + return perms; + } + + @Override + public Plugin getPlugin() { + return owner != null ? owner : permissible.getPlugin(); + } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/processors/AttachmentProcessor.java b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableTransientNode.java similarity index 58% rename from bukkit/src/main/java/me/lucko/luckperms/bukkit/processors/AttachmentProcessor.java rename to common/src/main/java/me/lucko/luckperms/common/node/ImmutableTransientNode.java index 9e25b185..0c9d7ba6 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/processors/AttachmentProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableTransientNode.java @@ -23,42 +23,39 @@ * SOFTWARE. */ -package me.lucko.luckperms.bukkit.processors; +package me.lucko.luckperms.common.node; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; +import lombok.experimental.Delegate; -import me.lucko.luckperms.api.Tristate; -import me.lucko.luckperms.common.processors.PermissionProcessor; - -import org.bukkit.permissions.PermissionAttachmentInfo; - -import java.util.Map; -import java.util.function.Supplier; +import me.lucko.luckperms.api.Node; /** - * Permission Processor for permissions set to a player via permission attachments. + * Holds a Node and plus an owning object. All calls are passed onto the contained Node instance. */ -@AllArgsConstructor -public class AttachmentProcessor implements PermissionProcessor { +@Getter +@ToString +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public final class ImmutableTransientNode implements Node { + public static ImmutableTransientNode of(@NonNull Node node, @NonNull Object owner) { + return new ImmutableTransientNode(node, owner); + } - @Getter - private final Supplier> map; + @Delegate + private final Node node; + private final Object owner; @Override - public Tristate hasPermission(String permission) { - Map m = map.get(); - if (m == null) { - return Tristate.UNDEFINED; - } - - PermissionAttachmentInfo pai = m.get(permission); - return pai == null ? Tristate.UNDEFINED : Tristate.fromBoolean(pai.getValue()); + public int hashCode() { + return node.hashCode(); } @Override - public void updateBacking(Map map) { - // Do nothing, this doesn't use the backing + public boolean equals(Object obj) { + return this == obj || node.equals(obj); } - }