Add ContextUpdateEvent and related functionality

This commit is contained in:
Luck 2020-05-25 21:49:49 +01:00
parent 5c0d82f306
commit 6b3e4bdc71
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
30 changed files with 446 additions and 110 deletions

View File

@ -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.
*
* <p>It is not strictly necessary to make a call to this method every time a context
* changes.</p>
*
* @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);
}
}

View File

@ -0,0 +1,76 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.
*
* <p>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.</p>
*
* <p>It will always be fired following a call to
* {@link ContextManager#signalContextUpdate(Object)}.</p>
*
* <p>The {@link #getSubject() subject} is always an instance of the platform's subject type. See
* {@link ContextManager} for details.</p>
*
* <p>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.</p>
*
* @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 <T> the subject type
* @return the casted subject
*/
default <T> @NonNull Optional<T> getSubject(@NonNull Class<T> subjectClass) {
Object subject = getSubject();
return subjectClass.isInstance(subject) ? Optional.of(subjectClass.cast(subject)) : Optional.empty();
}
}

View File

@ -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> 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<String, Boolean> 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()) {

View File

@ -88,11 +88,7 @@ public class BukkitContextManager extends ContextManager<Player, Player> {
}
@Override
public void invalidateCache(Player subject) {
if (subject == null) {
throw new NullPointerException("subject");
}
protected void invalidateCache(Player subject) {
QueryOptionsCache<Player> cache = this.onlineSubjectCaches.getIfPresent(subject);
if (cache != null) {
cache.invalidate();

View File

@ -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<Player> {
public class WorldCalculator implements ContextCalculator<Player>, Listener {
private final LPBukkitPlugin plugin;
public WorldCalculator(LPBukkitPlugin plugin) {
@ -75,4 +79,9 @@ public class WorldCalculator implements ContextCalculator<Player> {
}
return builder.build();
}
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldChange(PlayerChangedWorldEvent e) {
this.plugin.getContextManager().signalContextUpdate(e.getPlayer());
}
}

View File

@ -0,0 +1,92 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<String, Boolean> 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));
}
}
}

View File

@ -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<Void> {

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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());

View File

@ -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<ProxiedPlayer> {
public class BackendServerCalculator implements ContextCalculator<ProxiedPlayer>, 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<ProxiedPlayer>
}
return builder.build();
}
@EventHandler
public void onServerSwitch(ServerSwitchEvent e) {
this.plugin.getContextManager().signalContextUpdate(e.getPlayer());
}
}

View File

@ -74,7 +74,7 @@ public class BungeeContextManager extends ContextManager<ProxiedPlayer, ProxiedP
}
@Override
public void invalidateCache(ProxiedPlayer subject) {
protected void invalidateCache(ProxiedPlayer subject) {
this.contextsCache.invalidate(subject);
}

View File

@ -38,13 +38,13 @@ import me.lucko.luckperms.common.api.implementation.ApiQueryOptionsRegistry;
import me.lucko.luckperms.common.api.implementation.ApiTrackManager;
import me.lucko.luckperms.common.api.implementation.ApiUserManager;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.event.AbstractEventBus;
import me.lucko.luckperms.common.messaging.LuckPermsMessagingService;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.actionlog.ActionLogger;
import net.luckperms.api.context.ContextManager;
import net.luckperms.api.event.EventBus;
import net.luckperms.api.messaging.MessagingService;
import net.luckperms.api.messenger.MessengerProvider;
import net.luckperms.api.metastacking.MetaStackFactory;
@ -140,7 +140,7 @@ public class LuckPermsApiProvider implements LuckPerms {
}
@Override
public @NonNull EventBus getEventBus() {
public @NonNull AbstractEventBus<?> getEventBus() {
return this.plugin.getEventDispatcher().getEventBus();
}

View File

@ -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));
}
}

View File

@ -100,7 +100,19 @@ public abstract class ContextManager<S, P extends S> {
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<? super S> calculator) {
// calculators registered first should have priority (and be checked last.)

View File

@ -88,6 +88,10 @@ public abstract class AbstractEventBus<P> implements EventBus, AutoCloseable {
return this.bus.hasSubscribers(eventClass);
}
public void subscribe(LuckPermsEventListener listener) {
listener.bind(this);
}
@Override
public @NonNull <T extends LuckPermsEvent> EventSubscription<T> subscribe(@NonNull Class<T> eventClass, @NonNull Consumer<? super T> handler) {
Objects.requireNonNull(eventClass, "eventClass");

View File

@ -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));
}

View File

@ -0,0 +1,38 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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);
}

View File

@ -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> 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)

View File

@ -88,11 +88,7 @@ public class NukkitContextManager extends ContextManager<Player, Player> {
}
@Override
public void invalidateCache(Player subject) {
if (subject == null) {
throw new NullPointerException("subject");
}
protected void invalidateCache(Player subject) {
QueryOptionsCache<Player> cache = this.onlineSubjectCaches.getIfPresent(subject);
if (cache != null) {
cache.invalidate();

View File

@ -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<Player> {
public class WorldCalculator implements ContextCalculator<Player>, Listener {
private final LPNukkitPlugin plugin;
public WorldCalculator(LPNukkitPlugin plugin) {
@ -70,4 +74,12 @@ public class WorldCalculator implements ContextCalculator<Player> {
}
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);
}
}
}

View File

@ -0,0 +1,84 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<String, Boolean> permData = user.getCachedData().getPermissionData(queryOptions).getPermissionMap();
value = permData.getOrDefault(NODE, false);
} else {
value = false;
}
player.setOp(value);
}
}

View File

@ -166,7 +166,7 @@ public class NukkitConnectionListener extends AbstractConnectionListener impleme
return;
}
this.plugin.refreshAutoOp(player);
this.plugin.getContextManager().signalContextUpdate(player);
}
@EventHandler(priority = EventPriority.MONITOR)

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -67,11 +67,7 @@ public class SpongeContextManager extends ContextManager<Subject, Player> {
}
@Override
public void invalidateCache(Subject subject) {
if (subject == null) {
throw new NullPointerException("subject");
}
protected void invalidateCache(Subject subject) {
QueryOptionsCache<Subject> cache = this.subjectCaches.getIfPresent(subject);
if (cache != null) {
cache.invalidate();

View File

@ -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<Subject> {
}
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);
}
}

View File

@ -64,7 +64,8 @@ public class ContextCalculatorProxy implements ForwardingContextCalculator<Subje
@Override
public boolean add(Context context) {
if (context.getKey().isEmpty() || context.getValue().isEmpty()) {
if (!net.luckperms.api.context.Context.isValidKey(context.getKey()) ||
!net.luckperms.api.context.Context.isValidValue(context.getValue())) {
return false;
}
this.consumer.accept(context.getKey(), context.getValue());

View File

@ -25,6 +25,8 @@
package me.lucko.luckperms.velocity.context;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
@ -75,4 +77,9 @@ public class BackendServerCalculator implements ContextCalculator<Player> {
}
return builder.build();
}
@Subscribe
public void onServerConnect(ServerConnectedEvent e) {
this.plugin.getContextManager().signalContextUpdate(e.getPlayer());
}
}

View File

@ -65,11 +65,7 @@ public class VelocityContextManager extends ContextManager<Player, Player> {
}
@Override
public void invalidateCache(Player subject) {
if (subject == null) {
throw new NullPointerException("subject");
}
protected void invalidateCache(Player subject) {
QueryOptionsCache<Player> cache = this.subjectCaches.getIfPresent(subject);
if (cache != null) {
cache.invalidate();