diff --git a/api/src/main/java/net/luckperms/api/context/ContextManager.java b/api/src/main/java/net/luckperms/api/context/ContextManager.java index 5ce4935af..00d4b894f 100644 --- a/api/src/main/java/net/luckperms/api/context/ContextManager.java +++ b/api/src/main/java/net/luckperms/api/context/ContextManager.java @@ -138,6 +138,18 @@ public interface ContextManager { */ void unregisterCalculator(@NonNull ContextCalculator calculator); + /** + * Signal to the {@link ContextManager} that a {@code subject}s + * current contexts have changed. + * + *

It is not strictly necessary to make a call to this method every time a context + * changes.

+ * + * @param subject the subject + * @since 5.2 + */ + void signalContextUpdate(@NonNull Object subject); + /** * Gets the {@link ContextSetFactory}, responsible for creating * {@link ContextSet} instances. @@ -150,7 +162,11 @@ public interface ContextManager { * Invalidates the lookup cache for a given subject * * @param subject the subject + * @deprecated use {@link #signalContextUpdate(Object)} */ - void invalidateCache(@NonNull Object subject); + @Deprecated + default void invalidateCache(@NonNull Object subject) { + signalContextUpdate(subject); + } } diff --git a/api/src/main/java/net/luckperms/api/event/context/ContextUpdateEvent.java b/api/src/main/java/net/luckperms/api/event/context/ContextUpdateEvent.java new file mode 100644 index 000000000..8d35fff29 --- /dev/null +++ b/api/src/main/java/net/luckperms/api/event/context/ContextUpdateEvent.java @@ -0,0 +1,76 @@ +/* + * 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 net.luckperms.api.event.context; + +import net.luckperms.api.context.ContextManager; +import net.luckperms.api.event.LuckPermsEvent; +import net.luckperms.api.event.util.Param; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Optional; + +/** + * Called when a subject's current/active contexts are updated. + * + *

There are no guarantees that this event will be called for every update. It is merely to be + * used as a "hint" for plugins which depend on the current active contexts for a player.

+ * + *

It will always be fired following a call to + * {@link ContextManager#signalContextUpdate(Object)}.

+ * + *

The {@link #getSubject() subject} is always an instance of the platform's subject type. See + * {@link ContextManager} for details.

+ * + *

Unlike most other LuckPerms events, this event is not fired asynchronously. Care should be + * taken to ensure listeners are lightweight. Additionally, listeners should ensure they do not + * cause further updates to player context, thus possibly causing a stack overflow.

+ * + * @since 5.2 + */ +public interface ContextUpdateEvent extends LuckPermsEvent { + + /** + * Gets the subject whose contexts were updated. + * + * @return the subject + */ + @Param(0) + @NonNull Object getSubject(); + + /** + * Gets the subject whose contexts were updated, casted to a given type. + * + * @param subjectClass the type to cast to + * @param the subject type + * @return the casted subject + */ + default @NonNull Optional getSubject(@NonNull Class subjectClass) { + Object subject = getSubject(); + return subjectClass.isInstance(subject) ? Optional.of(subjectClass.cast(subject)) : Optional.empty(); + } + +} 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 d9b48700b..73adf03bd 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java @@ -38,13 +38,13 @@ import me.lucko.luckperms.bukkit.inject.server.InjectorSubscriptionMap; import me.lucko.luckperms.bukkit.inject.server.LuckPermsDefaultsMap; import me.lucko.luckperms.bukkit.inject.server.LuckPermsPermissionMap; import me.lucko.luckperms.bukkit.inject.server.LuckPermsSubscriptionMap; +import me.lucko.luckperms.bukkit.listeners.BukkitAutoOpListener; import me.lucko.luckperms.bukkit.listeners.BukkitCommandListUpdater; import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener; import me.lucko.luckperms.bukkit.listeners.BukkitPlatformListener; import me.lucko.luckperms.bukkit.messaging.BukkitMessagingFactory; import me.lucko.luckperms.bukkit.vault.VaultHookManager; import me.lucko.luckperms.common.api.LuckPermsApiProvider; -import me.lucko.luckperms.common.api.implementation.ApiUser; import me.lucko.luckperms.common.calculator.CalculatorFactory; import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.config.ConfigKeys; @@ -63,7 +63,6 @@ import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import net.luckperms.api.LuckPerms; -import net.luckperms.api.event.user.UserDataRecalculateEvent; import net.luckperms.api.query.QueryOptions; import org.bukkit.OfflinePlayer; @@ -75,7 +74,6 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.ServicePriority; import java.io.File; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -183,7 +181,10 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin { @Override protected void setupContextManager() { this.contextManager = new BukkitContextManager(this); - this.contextManager.registerCalculator(new WorldCalculator(this)); + + WorldCalculator worldCalculator = new WorldCalculator(this); + this.bootstrap.getServer().getPluginManager().registerEvents(worldCalculator, this.bootstrap); + this.contextManager.registerCalculator(worldCalculator); } @Override @@ -267,17 +268,12 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin { // register autoop listener if (getConfiguration().get(ConfigKeys.AUTO_OP)) { - getApiProvider().getEventBus().subscribe(UserDataRecalculateEvent.class, event -> { - User user = ApiUser.cast(event.getUser()); - Optional player = getBootstrap().getPlayer(user.getUniqueId()); - player.ifPresent(p -> refreshAutoOp(p, false)); - }); + getApiProvider().getEventBus().subscribe(new BukkitAutoOpListener(this)); } // register bukkit command list updater if (getConfiguration().get(ConfigKeys.UPDATE_CLIENT_COMMAND_LIST) && BukkitCommandListUpdater.isSupported()) { - BukkitCommandListUpdater commandListUpdater = new BukkitCommandListUpdater(this); - getApiProvider().getEventBus().subscribe(UserDataRecalculateEvent.class, commandListUpdater::onUserDataRecalculate); + getApiProvider().getEventBus().subscribe(new BukkitCommandListUpdater(this)); } // Load any online users (in the case of a reload) @@ -335,32 +331,6 @@ public class LPBukkitPlugin extends AbstractLuckPermsPlugin { } } - public void refreshAutoOp(Player player, boolean callerIsSync) { - if (!getConfiguration().get(ConfigKeys.AUTO_OP)) { - return; - } - - if (!callerIsSync && this.bootstrap.isServerStopping()) { - return; - } - - User user = getUserManager().getIfLoaded(player.getUniqueId()); - boolean value; - - if (user != null) { - Map permData = user.getCachedData().getPermissionData(this.contextManager.getQueryOptions(player)).getPermissionMap(); - value = permData.getOrDefault("luckperms.autoop", false); - } else { - value = false; - } - - if (callerIsSync) { - player.setOp(value); - } else { - this.bootstrap.getScheduler().executeSync(() -> player.setOp(value)); - } - } - private File resolveConfig() { File configFile = new File(this.bootstrap.getDataFolder(), "config.yml"); if (!configFile.exists()) { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/BukkitContextManager.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/BukkitContextManager.java index 32c2212e6..738b3cfef 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/BukkitContextManager.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/BukkitContextManager.java @@ -88,11 +88,7 @@ public class BukkitContextManager extends ContextManager { } @Override - public void invalidateCache(Player subject) { - if (subject == null) { - throw new NullPointerException("subject"); - } - + protected void invalidateCache(Player subject) { QueryOptionsCache cache = this.onlineSubjectCaches.getIfPresent(subject); if (cache != null) { cache.invalidate(); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/WorldCalculator.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/WorldCalculator.java index 938420829..e4eae67de 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/WorldCalculator.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/WorldCalculator.java @@ -38,13 +38,17 @@ import net.luckperms.api.context.ImmutableContextSet; import org.bukkit.World; 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 org.checkerframework.checker.nullness.qual.NonNull; import java.util.HashSet; import java.util.List; import java.util.Set; -public class WorldCalculator implements ContextCalculator { +public class WorldCalculator implements ContextCalculator, Listener { private final LPBukkitPlugin plugin; public WorldCalculator(LPBukkitPlugin plugin) { @@ -75,4 +79,9 @@ public class WorldCalculator implements ContextCalculator { } return builder.build(); } + + @EventHandler(priority = EventPriority.LOWEST) + public void onWorldChange(PlayerChangedWorldEvent e) { + this.plugin.getContextManager().signalContextUpdate(e.getPlayer()); + } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitAutoOpListener.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitAutoOpListener.java new file mode 100644 index 000000000..249d6a377 --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitAutoOpListener.java @@ -0,0 +1,92 @@ +/* + * 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.listeners; + +import me.lucko.luckperms.bukkit.LPBukkitPlugin; +import me.lucko.luckperms.common.api.implementation.ApiUser; +import me.lucko.luckperms.common.event.LuckPermsEventListener; +import me.lucko.luckperms.common.model.User; + +import net.luckperms.api.event.EventBus; +import net.luckperms.api.event.context.ContextUpdateEvent; +import net.luckperms.api.event.user.UserDataRecalculateEvent; +import net.luckperms.api.query.QueryOptions; + +import org.bukkit.entity.Player; + +import java.util.Map; + +/** + * Implements the LuckPerms auto op feature. + */ +public class BukkitAutoOpListener implements LuckPermsEventListener { + private static final String NODE = "luckperms.autoop"; + + private final LPBukkitPlugin plugin; + + public BukkitAutoOpListener(LPBukkitPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void bind(EventBus bus) { + bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate); + bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate); + } + + private void onUserDataRecalculate(UserDataRecalculateEvent e) { + User user = ApiUser.cast(e.getUser()); + this.plugin.getBootstrap().getPlayer(user.getUniqueId()).ifPresent(p -> refreshAutoOp(p, false)); + } + + private void onContextUpdate(ContextUpdateEvent e) { + e.getSubject(Player.class).ifPresent(p -> refreshAutoOp(p, true)); + } + + private void refreshAutoOp(Player player, boolean callerIsSync) { + if (!callerIsSync && this.plugin.getBootstrap().isServerStopping()) { + return; + } + + User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId()); + + boolean value; + if (user != null) { + QueryOptions queryOptions = this.plugin.getContextManager().getQueryOptions(player); + Map permData = user.getCachedData().getPermissionData(queryOptions).getPermissionMap(); + value = permData.getOrDefault(NODE, false); + } else { + value = false; + } + + if (callerIsSync) { + player.setOp(value); + } else { + this.plugin.getBootstrap().getScheduler().executeSync(() -> player.setOp(value)); + } + } + +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitCommandListUpdater.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitCommandListUpdater.java index 2708730f3..330b55e96 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitCommandListUpdater.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitCommandListUpdater.java @@ -29,8 +29,11 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.common.cache.BufferedRequest; +import me.lucko.luckperms.common.event.LuckPermsEventListener; import me.lucko.luckperms.common.util.CaffeineFactory; +import net.luckperms.api.event.EventBus; +import net.luckperms.api.event.context.ContextUpdateEvent; import net.luckperms.api.event.user.UserDataRecalculateEvent; import org.bukkit.entity.Player; @@ -41,7 +44,7 @@ import java.util.concurrent.TimeUnit; /** * Calls {@link Player#updateCommands()} when a players permissions change. */ -public class BukkitCommandListUpdater { +public class BukkitCommandListUpdater implements LuckPermsEventListener { public static boolean isSupported() { try { @@ -61,13 +64,25 @@ public class BukkitCommandListUpdater { this.plugin = plugin; } - // Called when a user's data is recalculated. - public void onUserDataRecalculate(UserDataRecalculateEvent e) { + @Override + public void bind(EventBus bus) { + bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate); + bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate); + } + + private void onUserDataRecalculate(UserDataRecalculateEvent e) { + requestUpdate(e.getUser().getUniqueId()); + } + + private void onContextUpdate(ContextUpdateEvent e) { + e.getSubject(Player.class).ifPresent(p -> requestUpdate(p.getUniqueId())); + } + + private void requestUpdate(UUID uniqueId) { if (this.plugin.getBootstrap().isServerStopping()) { return; } - UUID uniqueId = e.getUser().getUniqueId(); if (!this.plugin.getBootstrap().isPlayerOnline(uniqueId)) { return; } @@ -82,12 +97,8 @@ public class BukkitCommandListUpdater { return; } - this.plugin.getBootstrap().getScheduler().sync().execute(() -> { - Player player = this.plugin.getBootstrap().getPlayer(uniqueId).orElse(null); - if (player != null) { - player.updateCommands(); - } - }); + this.plugin.getBootstrap().getScheduler().sync() + .execute(() -> this.plugin.getBootstrap().getPlayer(uniqueId).ifPresent(Player::updateCommands)); } private final class SendBuffer extends BufferedRequest { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitConnectionListener.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitConnectionListener.java index 97d34a57f..287d41f0f 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitConnectionListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitConnectionListener.java @@ -201,7 +201,7 @@ public class BukkitConnectionListener extends AbstractConnectionListener impleme return; } - this.plugin.refreshAutoOp(player, true); + this.plugin.getContextManager().signalContextUpdate(player); } @EventHandler(priority = EventPriority.MONITOR) diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitPlatformListener.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitPlatformListener.java index 28764a628..8c3ee43d7 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitPlatformListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitPlatformListener.java @@ -32,9 +32,7 @@ import me.lucko.luckperms.common.locale.message.Message; import org.bukkit.command.CommandSender; import org.bukkit.event.Cancellable; import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.server.PluginEnableEvent; import org.bukkit.event.server.RemoteServerCommandEvent; @@ -92,10 +90,4 @@ public class BukkitPlatformListener implements Listener { } } - @EventHandler(priority = EventPriority.LOWEST) - public void onWorldChange(PlayerChangedWorldEvent e) { - this.plugin.getContextManager().invalidateCache(e.getPlayer()); - this.plugin.refreshAutoOp(e.getPlayer(), true); - } - } diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java index 49baa86a8..b9a18a770 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java @@ -138,7 +138,10 @@ public class LPBungeePlugin extends AbstractLuckPermsPlugin { @Override protected void setupContextManager() { this.contextManager = new BungeeContextManager(this); - this.contextManager.registerCalculator(new BackendServerCalculator(this)); + + BackendServerCalculator backendServerCalculator = new BackendServerCalculator(this); + this.bootstrap.getProxy().getPluginManager().registerListener(this.bootstrap, backendServerCalculator); + this.contextManager.registerCalculator(backendServerCalculator); if (this.bootstrap.getProxy().getPluginManager().getPlugin("RedisBungee") != null) { this.contextManager.registerCalculator(new RedisBungeeCalculator()); diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/context/BackendServerCalculator.java b/bungee/src/main/java/me/lucko/luckperms/bungee/context/BackendServerCalculator.java index 05b749ecd..5331de953 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/context/BackendServerCalculator.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/context/BackendServerCalculator.java @@ -36,6 +36,9 @@ import net.luckperms.api.context.DefaultContextKeys; import net.luckperms.api.context.ImmutableContextSet; import net.md_5.bungee.api.config.ServerInfo; 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 org.checkerframework.checker.nullness.qual.NonNull; @@ -43,7 +46,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; -public class BackendServerCalculator implements ContextCalculator { +public class BackendServerCalculator implements ContextCalculator, Listener { private static String getServer(ProxiedPlayer player) { return player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName().toLowerCase()); @@ -74,4 +77,9 @@ public class BackendServerCalculator implements ContextCalculator } return builder.build(); } + + @EventHandler + public void onServerSwitch(ServerSwitchEvent e) { + this.plugin.getContextManager().signalContextUpdate(e.getPlayer()); + } } diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/context/BungeeContextManager.java b/bungee/src/main/java/me/lucko/luckperms/bungee/context/BungeeContextManager.java index ff8072b70..3050dbdc0 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/context/BungeeContextManager.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/context/BungeeContextManager.java @@ -74,7 +74,7 @@ public class BungeeContextManager extends ContextManager getEventBus() { return this.plugin.getEventDispatcher().getEventBus(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiContextManager.java b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiContextManager.java index 0bd646c93..58d7bf3d0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiContextManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiContextManager.java @@ -116,8 +116,8 @@ public class ApiContextManager implements net.luckperms.api.context.ContextManag } @Override - public void invalidateCache(@NonNull Object subject) { + public void signalContextUpdate(@NonNull Object subject) { Objects.requireNonNull(subject, "subject"); - this.handle.invalidateCache(checkType(subject)); + this.handle.signalContextUpdate(checkType(subject)); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/context/ContextManager.java b/common/src/main/java/me/lucko/luckperms/common/context/ContextManager.java index b9de1f4fc..dd806c8c9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/ContextManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/ContextManager.java @@ -100,7 +100,19 @@ public abstract class ContextManager { public abstract QueryOptions formQueryOptions(S subject, ImmutableContextSet contextSet); - public abstract void invalidateCache(S subject); + public void signalContextUpdate(S subject) { + if (subject == null) { + throw new NullPointerException("subject"); + } + + // invalidate their cache + invalidateCache(subject); + + // call event + this.plugin.getEventDispatcher().dispatchContextUpdate(subject); + } + + protected abstract void invalidateCache(S subject); public void registerCalculator(ContextCalculator calculator) { // calculators registered first should have priority (and be checked last.) diff --git a/common/src/main/java/me/lucko/luckperms/common/event/AbstractEventBus.java b/common/src/main/java/me/lucko/luckperms/common/event/AbstractEventBus.java index 8876763f0..9ecb810f6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/event/AbstractEventBus.java +++ b/common/src/main/java/me/lucko/luckperms/common/event/AbstractEventBus.java @@ -88,6 +88,10 @@ public abstract class AbstractEventBus

implements EventBus, AutoCloseable { return this.bus.hasSubscribers(eventClass); } + public void subscribe(LuckPermsEventListener listener) { + listener.bind(this); + } + @Override public @NonNull EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer handler) { Objects.requireNonNull(eventClass, "eventClass"); diff --git a/common/src/main/java/me/lucko/luckperms/common/event/EventDispatcher.java b/common/src/main/java/me/lucko/luckperms/common/event/EventDispatcher.java index 08e98fc2e..deca7003c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/event/EventDispatcher.java +++ b/common/src/main/java/me/lucko/luckperms/common/event/EventDispatcher.java @@ -46,6 +46,7 @@ import net.luckperms.api.actionlog.Action; import net.luckperms.api.event.LuckPermsEvent; import net.luckperms.api.event.cause.CreationCause; import net.luckperms.api.event.cause.DeletionCause; +import net.luckperms.api.event.context.ContextUpdateEvent; import net.luckperms.api.event.extension.ExtensionLoadEvent; import net.luckperms.api.event.group.GroupCacheLoadEvent; import net.luckperms.api.event.group.GroupCreateEvent; @@ -138,6 +139,14 @@ public final class EventDispatcher { } } + public void dispatchContextUpdate(Object subject) { + if (!shouldPost(ContextUpdateEvent.class)) { + return; + } + + post(generate(ContextUpdateEvent.class, subject)); + } + public void dispatchExtensionLoad(Extension extension) { post(ExtensionLoadEvent.class, () -> generate(ExtensionLoadEvent.class, extension)); } diff --git a/common/src/main/java/me/lucko/luckperms/common/event/LuckPermsEventListener.java b/common/src/main/java/me/lucko/luckperms/common/event/LuckPermsEventListener.java new file mode 100644 index 000000000..4f4ec512c --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/event/LuckPermsEventListener.java @@ -0,0 +1,38 @@ +/* + * 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.common.event; + +import net.luckperms.api.event.EventBus; +import net.luckperms.api.event.LuckPermsEvent; + +/** + * Defines a class which listens to {@link LuckPermsEvent}s. + */ +public interface LuckPermsEventListener { + + void bind(EventBus bus); + +} diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java index 0fb8a0d08..eade58ed1 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java @@ -26,7 +26,6 @@ package me.lucko.luckperms.nukkit; import me.lucko.luckperms.common.api.LuckPermsApiProvider; -import me.lucko.luckperms.common.api.implementation.ApiUser; import me.lucko.luckperms.common.calculator.CalculatorFactory; import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.config.ConfigKeys; @@ -55,11 +54,11 @@ import me.lucko.luckperms.nukkit.inject.server.InjectorSubscriptionMap; import me.lucko.luckperms.nukkit.inject.server.LuckPermsDefaultsMap; import me.lucko.luckperms.nukkit.inject.server.LuckPermsPermissionMap; import me.lucko.luckperms.nukkit.inject.server.LuckPermsSubscriptionMap; +import me.lucko.luckperms.nukkit.listeners.NukkitAutoOpListener; import me.lucko.luckperms.nukkit.listeners.NukkitConnectionListener; import me.lucko.luckperms.nukkit.listeners.NukkitPlatformListener; import net.luckperms.api.LuckPerms; -import net.luckperms.api.event.user.UserDataRecalculateEvent; import net.luckperms.api.query.QueryOptions; import cn.nukkit.Player; @@ -145,7 +144,10 @@ public class LPNukkitPlugin extends AbstractLuckPermsPlugin { @Override protected void setupContextManager() { this.contextManager = new NukkitContextManager(this); - this.contextManager.registerCalculator(new WorldCalculator(this)); + + WorldCalculator worldCalculator = new WorldCalculator(this); + this.bootstrap.getServer().getPluginManager().registerEvents(worldCalculator, this.bootstrap); + this.contextManager.registerCalculator(worldCalculator); } @Override @@ -205,11 +207,7 @@ public class LPNukkitPlugin extends AbstractLuckPermsPlugin { // register autoop listener if (getConfiguration().get(ConfigKeys.AUTO_OP)) { - getApiProvider().getEventBus().subscribe(UserDataRecalculateEvent.class, event -> { - User user = ApiUser.cast(event.getUser()); - Optional player = getBootstrap().getPlayer(user.getUniqueId()); - player.ifPresent(this::refreshAutoOp); - }); + getApiProvider().getEventBus().subscribe(new NukkitAutoOpListener(this)); } // Load any online users (in the case of a reload) diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitContextManager.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitContextManager.java index 420050523..9514862af 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitContextManager.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitContextManager.java @@ -88,11 +88,7 @@ public class NukkitContextManager extends ContextManager { } @Override - public void invalidateCache(Player subject) { - if (subject == null) { - throw new NullPointerException("subject"); - } - + protected void invalidateCache(Player subject) { QueryOptionsCache cache = this.onlineSubjectCaches.getIfPresent(subject); if (cache != null) { cache.invalidate(); diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/WorldCalculator.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/WorldCalculator.java index 6e8ab6015..9fd23cc63 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/WorldCalculator.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/WorldCalculator.java @@ -38,13 +38,17 @@ import net.luckperms.api.context.ImmutableContextSet; import org.checkerframework.checker.nullness.qual.NonNull; import cn.nukkit.Player; +import cn.nukkit.event.EventHandler; +import cn.nukkit.event.EventPriority; +import cn.nukkit.event.Listener; +import cn.nukkit.event.entity.EntityLevelChangeEvent; import cn.nukkit.level.Level; import java.util.Collection; import java.util.HashSet; import java.util.Set; -public class WorldCalculator implements ContextCalculator { +public class WorldCalculator implements ContextCalculator, Listener { private final LPNukkitPlugin plugin; public WorldCalculator(LPNukkitPlugin plugin) { @@ -70,4 +74,12 @@ public class WorldCalculator implements ContextCalculator { } return builder.build(); } + + @EventHandler(priority = EventPriority.LOWEST) + public void onWorldChange(EntityLevelChangeEvent e) { + if (e.getEntity() instanceof Player) { + Player player = (Player) e.getEntity(); + this.plugin.getContextManager().signalContextUpdate(player); + } + } } diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitAutoOpListener.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitAutoOpListener.java new file mode 100644 index 000000000..50ee3ffce --- /dev/null +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitAutoOpListener.java @@ -0,0 +1,84 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.nukkit.listeners; + +import me.lucko.luckperms.common.api.implementation.ApiUser; +import me.lucko.luckperms.common.event.LuckPermsEventListener; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.nukkit.LPNukkitPlugin; + +import net.luckperms.api.event.EventBus; +import net.luckperms.api.event.context.ContextUpdateEvent; +import net.luckperms.api.event.user.UserDataRecalculateEvent; +import net.luckperms.api.query.QueryOptions; + +import cn.nukkit.Player; + +import java.util.Map; + +/** + * Implements the LuckPerms auto op feature. + */ +public class NukkitAutoOpListener implements LuckPermsEventListener { + private static final String NODE = "luckperms.autoop"; + + private final LPNukkitPlugin plugin; + + public NukkitAutoOpListener(LPNukkitPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void bind(EventBus bus) { + bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate); + bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate); + } + + private void onUserDataRecalculate(UserDataRecalculateEvent e) { + User user = ApiUser.cast(e.getUser()); + this.plugin.getBootstrap().getPlayer(user.getUniqueId()).ifPresent(this::refreshAutoOp); + } + + private void onContextUpdate(ContextUpdateEvent e) { + e.getSubject(Player.class).ifPresent(this::refreshAutoOp); + } + + private void refreshAutoOp(Player player) { + User user = plugin.getUserManager().getIfLoaded(player.getUniqueId()); + boolean value; + + if (user != null) { + QueryOptions queryOptions = this.plugin.getContextManager().getQueryOptions(player); + Map permData = user.getCachedData().getPermissionData(queryOptions).getPermissionMap(); + value = permData.getOrDefault(NODE, false); + } else { + value = false; + } + + player.setOp(value); + } + +} diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitConnectionListener.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitConnectionListener.java index 2f6f7aac4..cf3d38dff 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitConnectionListener.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitConnectionListener.java @@ -166,7 +166,7 @@ public class NukkitConnectionListener extends AbstractConnectionListener impleme return; } - this.plugin.refreshAutoOp(player); + this.plugin.getContextManager().signalContextUpdate(player); } @EventHandler(priority = EventPriority.MONITOR) diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitPlatformListener.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitPlatformListener.java index 4b1770463..9dc6302be 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitPlatformListener.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/listeners/NukkitPlatformListener.java @@ -29,13 +29,10 @@ import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.nukkit.LPNukkitPlugin; -import cn.nukkit.Player; import cn.nukkit.command.CommandSender; import cn.nukkit.event.Cancellable; import cn.nukkit.event.EventHandler; -import cn.nukkit.event.EventPriority; import cn.nukkit.event.Listener; -import cn.nukkit.event.entity.EntityLevelChangeEvent; import cn.nukkit.event.player.PlayerCommandPreprocessEvent; import cn.nukkit.event.server.RemoteServerCommandEvent; import cn.nukkit.event.server.ServerCommandEvent; @@ -85,13 +82,4 @@ public class NukkitPlatformListener implements Listener { } } - @EventHandler(priority = EventPriority.LOWEST) - public void onWorldChange(EntityLevelChangeEvent e) { - if (e.getEntity() instanceof Player) { - Player player = (Player) e.getEntity(); - this.plugin.getContextManager().invalidateCache(player); - this.plugin.refreshAutoOp(player); - } - } - } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java index 5cc296478..6e6789224 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java @@ -156,7 +156,10 @@ public class LPSpongePlugin extends AbstractLuckPermsPlugin { @Override protected void setupContextManager() { this.contextManager = new SpongeContextManager(this); - this.contextManager.registerCalculator(new WorldCalculator(this)); + + WorldCalculator worldCalculator = new WorldCalculator(this); + this.bootstrap.getGame().getEventManager().registerListeners(this.bootstrap, worldCalculator); + this.contextManager.registerCalculator(worldCalculator); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongeContextManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongeContextManager.java index b3518b32e..90b415837 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongeContextManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongeContextManager.java @@ -67,11 +67,7 @@ public class SpongeContextManager extends ContextManager { } @Override - public void invalidateCache(Subject subject) { - if (subject == null) { - throw new NullPointerException("subject"); - } - + protected void invalidateCache(Subject subject) { QueryOptionsCache cache = this.subjectCaches.getIfPresent(subject); if (cache != null) { cache.invalidate(); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/context/WorldCalculator.java b/sponge/src/main/java/me/lucko/luckperms/sponge/context/WorldCalculator.java index d1024becb..129305092 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/context/WorldCalculator.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/context/WorldCalculator.java @@ -38,7 +38,11 @@ import net.luckperms.api.context.ImmutableContextSet; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.Game; import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.entity.MoveEntityEvent; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.world.World; @@ -84,4 +88,19 @@ public class WorldCalculator implements ContextCalculator { } return builder.build(); } + + @Listener(order = Order.LAST) + public void onWorldChange(MoveEntityEvent.Teleport e) { + Entity targetEntity = e.getTargetEntity(); + if (!(targetEntity instanceof Player)) { + return; + } + + if (e.getFromTransform().getExtent().equals(e.getToTransform().getExtent())) { + return; + } + + Player player = (Player) targetEntity; + this.plugin.getContextManager().signalContextUpdate(player); + } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/ContextCalculatorProxy.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/ContextCalculatorProxy.java index 221cf4ea1..9fd13f63c 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/ContextCalculatorProxy.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/ContextCalculatorProxy.java @@ -64,7 +64,8 @@ public class ContextCalculatorProxy implements ForwardingContextCalculator { } return builder.build(); } + + @Subscribe + public void onServerConnect(ServerConnectedEvent e) { + this.plugin.getContextManager().signalContextUpdate(e.getPlayer()); + } } diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityContextManager.java b/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityContextManager.java index b619da2ed..ee76620ff 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityContextManager.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityContextManager.java @@ -65,11 +65,7 @@ public class VelocityContextManager extends ContextManager { } @Override - public void invalidateCache(Player subject) { - if (subject == null) { - throw new NullPointerException("subject"); - } - + protected void invalidateCache(Player subject) { QueryOptionsCache cache = this.subjectCaches.getIfPresent(subject); if (cache != null) { cache.invalidate();