From f5cdb98b01976a6e26600bbddbe51918efd47ca2 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 19 May 2021 12:31:48 +0100 Subject: [PATCH 001/314] Fix ExpiringSet not really working & causing memory leaks (#3027) --- .../messaging/LuckPermsMessagingService.java | 3 +- .../file/watcher/FileWatcher.java | 3 +- .../luckperms/common/util/ExpiringSet.java | 28 ++++++++++++------- velocity/build.gradle | 6 ++-- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/LuckPermsMessagingService.java b/common/src/main/java/me/lucko/luckperms/common/messaging/LuckPermsMessagingService.java index af7465c54..a49f283ea 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/LuckPermsMessagingService.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/LuckPermsMessagingService.java @@ -53,13 +53,12 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Objects; -import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; public class LuckPermsMessagingService implements InternalMessagingService, IncomingMessageConsumer { private final LuckPermsPlugin plugin; - private final Set receivedMessages; + private final ExpiringSet receivedMessages; private final PushUpdateBuffer updateBuffer; private final MessengerProvider messengerProvider; diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/watcher/FileWatcher.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/watcher/FileWatcher.java index 4a1ada46a..2aaddcd67 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/watcher/FileWatcher.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/watcher/FileWatcher.java @@ -36,7 +36,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -98,7 +97,7 @@ public static final class WatchedLocation { private final Path path; /** A set of files which have been modified recently */ - private final Set recentlyModifiedFiles = new ExpiringSet<>(4, TimeUnit.SECONDS); + private final ExpiringSet recentlyModifiedFiles = new ExpiringSet<>(4, TimeUnit.SECONDS); /** The listener callback functions */ private final List> callbacks = new CopyOnWriteArrayList<>(); diff --git a/common/src/main/java/me/lucko/luckperms/common/util/ExpiringSet.java b/common/src/main/java/me/lucko/luckperms/common/util/ExpiringSet.java index 70df598ab..8ed2535d9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/ExpiringSet.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/ExpiringSet.java @@ -26,10 +26,7 @@ package me.lucko.luckperms.common.util; import com.github.benmanes.caffeine.cache.Cache; -import com.google.common.collect.ForwardingSet; -import java.util.Collections; -import java.util.Set; import java.util.concurrent.TimeUnit; /** @@ -37,16 +34,27 @@ * * @param element type */ -public class ExpiringSet extends ForwardingSet { - private final Set setView; +public class ExpiringSet { + private final Cache cache; + private final long lifetime; public ExpiringSet(long duration, TimeUnit unit) { - Cache cache = CaffeineFactory.newBuilder().expireAfterAccess(duration, unit).build(); - this.setView = Collections.newSetFromMap(cache.asMap()); + this.cache = CaffeineFactory.newBuilder().expireAfterWrite(duration, unit).build(); + this.lifetime = unit.toMillis(duration); } - @Override - protected Set delegate() { - return this.setView; + public boolean add(E item) { + boolean present = contains(item); + this.cache.put(item, System.currentTimeMillis() + this.lifetime); + return !present; + } + + public boolean contains(E item) { + Long timeout = this.cache.getIfPresent(item); + return timeout != null && timeout > System.currentTimeMillis(); + } + + public void remove(E item) { + this.cache.invalidate(item); } } diff --git a/velocity/build.gradle b/velocity/build.gradle index 9ea150c14..652bcf608 100644 --- a/velocity/build.gradle +++ b/velocity/build.gradle @@ -4,14 +4,14 @@ plugins { } repositories { - maven { url 'https://repo.velocitypowered.com/snapshots/' } + maven { url 'https://repo.velocitypowered.com/releases/' } } dependencies { compile project(':common') - compileOnly 'com.velocitypowered:velocity-api:1.1.0-SNAPSHOT' - annotationProcessor 'com.velocitypowered:velocity-api:1.1.0-SNAPSHOT' + compileOnly 'com.velocitypowered:velocity-api:1.1.5' + annotationProcessor 'com.velocitypowered:velocity-api:1.1.5' } blossom { From 05c9ca5951830ab0e85e00a9d8071a17e95727da Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 23 May 2021 11:27:45 +0100 Subject: [PATCH 002/314] Properly cleanup JarInJarClassLoader when plugin disables --- .../common/loader/JarInJarClassLoader.java | 15 +++++++++++++++ .../common/plugin/AbstractLuckPermsPlugin.java | 3 +++ .../plugin/classpath/ClassPathAppender.java | 6 +++++- .../classpath/JarInJarClassPathAppender.java | 11 +++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/common/loader-utils/src/main/java/me/lucko/luckperms/common/loader/JarInJarClassLoader.java b/common/loader-utils/src/main/java/me/lucko/luckperms/common/loader/JarInJarClassLoader.java index 381952a3e..6bcf98269 100644 --- a/common/loader-utils/src/main/java/me/lucko/luckperms/common/loader/JarInJarClassLoader.java +++ b/common/loader-utils/src/main/java/me/lucko/luckperms/common/loader/JarInJarClassLoader.java @@ -33,6 +33,7 @@ import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; /** @@ -64,6 +65,20 @@ public void addJarToClasspath(URL url) { addURL(url); } + public void deleteJarResource() { + URL[] urls = getURLs(); + if (urls.length == 0) { + return; + } + + try { + Path path = Paths.get(urls[0].toURI()); + Files.deleteIfExists(path); + } catch (Exception e) { + // ignore + } + } + /** * Creates a new plugin instance. * diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 63576ab56..e8b2ef511 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -255,6 +255,9 @@ public final void disable() { // shutdown async executor pool getBootstrap().getScheduler().shutdownExecutor(); + // close classpath appender + getBootstrap().getClassPathAppender().close(); + getLogger().info("Goodbye!"); } diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ClassPathAppender.java b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ClassPathAppender.java index 5adf9690c..77fefcb14 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ClassPathAppender.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ClassPathAppender.java @@ -30,8 +30,12 @@ /** * Interface which allows access to add URLs to the plugin classpath at runtime. */ -public interface ClassPathAppender { +public interface ClassPathAppender extends AutoCloseable { void addJarToClasspath(Path file); + @Override + default void close() { + + } } diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/JarInJarClassPathAppender.java b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/JarInJarClassPathAppender.java index 9dd74ea63..1db23f5ae 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/JarInJarClassPathAppender.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/JarInJarClassPathAppender.java @@ -27,6 +27,7 @@ import me.lucko.luckperms.common.loader.JarInJarClassLoader; +import java.io.IOException; import java.net.MalformedURLException; import java.nio.file.Path; @@ -48,4 +49,14 @@ public void addJarToClasspath(Path file) { throw new RuntimeException(e); } } + + @Override + public void close() { + this.classLoader.deleteJarResource(); + try { + this.classLoader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } } From ad49508b745a74644a919c858540311098f91b42 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 23 May 2021 11:29:22 +0100 Subject: [PATCH 003/314] Some misc tidying up --- .../luckperms/bukkit/LPBukkitPlugin.java | 9 --- .../luckperms/bungee/LPBungeePlugin.java | 9 --- .../common/event/EventDispatcher.java | 58 ++++++++++--------- .../plugin/AbstractLuckPermsPlugin.java | 53 +++++++++++------ .../luckperms/fabric/LPFabricPlugin.java | 9 --- .../luckperms/nukkit/LPNukkitPlugin.java | 9 --- .../luckperms/sponge/LPSpongePlugin.java | 5 +- .../luckperms/velocity/LPVelocityPlugin.java | 9 --- 8 files changed, 65 insertions(+), 96 deletions(-) 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 44b322766..3e68c0e9b 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java @@ -60,8 +60,6 @@ import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin; import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; -import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import net.luckperms.api.LuckPerms; import net.luckperms.api.query.QueryOptions; @@ -79,7 +77,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.stream.Stream; /** @@ -259,12 +256,6 @@ protected void registerApiOnPlatform(LuckPerms api) { this.bootstrap.getServer().getServicesManager().register(LuckPerms.class, api, this.bootstrap.getLoader(), ServicePriority.Normal); } - @Override - protected void registerHousekeepingTasks() { - this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS); - this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES); - } - @Override protected void performFinalSetup() { // register permissions 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 171de9ad6..8875ebc53 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java @@ -47,8 +47,6 @@ import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin; import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; -import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import net.luckperms.api.LuckPerms; import net.luckperms.api.context.DefaultContextKeys; @@ -57,7 +55,6 @@ import java.util.Optional; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.stream.Stream; /** @@ -170,12 +167,6 @@ protected void registerApiOnPlatform(LuckPerms api) { // BungeeCord doesn't have a services manager } - @Override - protected void registerHousekeepingTasks() { - this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS); - this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES); - } - @Override protected void performFinalSetup() { 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 272794236..b56989967 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 @@ -115,13 +115,23 @@ public AbstractEventBus getEventBus() { return this.eventBus; } - private void postAsync(Class eventClass, Object... params) { - // check against common mistakes - events with any sort of result shouldn't be posted async - if (Cancellable.class.isAssignableFrom(eventClass)) { - throw new RuntimeException("Cancellable event cannot be posted async (" + eventClass + ")"); + private LuckPermsEvent generate(Class eventClass, Object... params) { + try { + return GeneratedEventClass.generate(eventClass).newInstance(this.eventBus.getApiProvider(), params); + } catch (Throwable e) { + throw new RuntimeException("Exception occurred whilst generating event instance", e); } - if (ResultEvent.class.isAssignableFrom(eventClass)) { - throw new RuntimeException("ResultEvent event cannot be posted async (" + eventClass + ")"); + } + + private void post(Class eventClass, Object... params) { + LuckPermsEvent event = generate(eventClass, params); + this.eventBus.post(event); + } + + private void postAsync(Class eventClass, Object... params) { + // check against common mistakes - events with any sort of result shouldn't be posted async + if (Cancellable.class.isAssignableFrom(eventClass) || ResultEvent.class.isAssignableFrom(eventClass)) { + throw new RuntimeException("Event cannot be posted async (" + eventClass.getName() + ")"); } // if there aren't any handlers registered for the event, don't bother trying to post it @@ -130,24 +140,24 @@ private void postAsync(Class eventClass, Object... } // async: generate an event class and post it - this.eventBus.getPlugin().getBootstrap().getScheduler().executeAsync(() -> { - T event = generate(eventClass, params); - this.eventBus.post(event); - }); + this.eventBus.getPlugin().getBootstrap().getScheduler().executeAsync(() -> post(eventClass, params)); } - private void postSync(Class eventClass, Object... params) { + private void postSync(Class eventClass, Object... params) { // if there aren't any handlers registered for our event, don't bother trying to post it if (!this.eventBus.shouldPost(eventClass)) { return; } // generate an event class and post it - T event = generate(eventClass, params); - this.eventBus.post(event); + post(eventClass, params); } - private boolean postCancellable(Class eventClass, Object... params) { + private boolean postCancellable(Class eventClass, Object... params) { + if (!Cancellable.class.isAssignableFrom(eventClass)) { + throw new RuntimeException("Event is not cancellable: " + eventClass.getName()); + } + // extract the initial state from the first parameter boolean initialState = (boolean) params[0]; @@ -159,23 +169,14 @@ private boolean postCancellable(Class T generate(Class eventClass, Object... params) { - try { - return (T) GeneratedEventClass.generate(eventClass).newInstance(this.eventBus.getApiProvider(), params); - } catch (Throwable e) { - throw new RuntimeException("Exception occurred whilst generating event instance", e); - } - } public void dispatchContextUpdate(Object subject) { postSync(ContextUpdateEvent.class, subject); @@ -390,8 +391,9 @@ private static ApiPermissionHolder proxy(PermissionHolder holder) { } } - public static List> getKnownEventTypes() { - return ImmutableList.of( + @SuppressWarnings("unchecked") + public static Class[] getKnownEventTypes() { + return new Class[]{ ContextUpdateEvent.class, ExtensionLoadEvent.class, GroupCacheLoadEvent.class, @@ -432,7 +434,7 @@ public static List> getKnownEventTypes() { UserUnloadEvent.class, UserDemoteEvent.class, UserPromoteEvent.class - ); + }; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index e8b2ef511..2ff3dc68d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -52,6 +52,8 @@ import me.lucko.luckperms.common.storage.StorageType; import me.lucko.luckperms.common.storage.implementation.file.watcher.FileWatcher; import me.lucko.luckperms.common.storage.misc.DataConstraints; +import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; +import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; @@ -195,9 +197,9 @@ public final void enable() { this.extensionManager.loadExtensions(getBootstrap().getConfigDirectory().resolve("extensions")); // schedule update tasks - int mins = getConfiguration().get(ConfigKeys.SYNC_TIME); - if (mins > 0) { - getBootstrap().getScheduler().asyncRepeating(() -> this.syncTaskBuffer.request(), mins, TimeUnit.MINUTES); + int syncMins = getConfiguration().get(ConfigKeys.SYNC_TIME); + if (syncMins > 0) { + getBootstrap().getScheduler().asyncRepeating(() -> this.syncTaskBuffer.request(), syncMins, TimeUnit.MINUTES); } // run an update instantly. @@ -261,6 +263,8 @@ public final void disable() { getLogger().info("Goodbye!"); } + // hooks called during load + protected Set getGlobalDependencies() { return EnumSet.of( Dependency.ADVENTURE, @@ -272,22 +276,11 @@ protected Set getGlobalDependencies() { ); } - protected Path resolveConfig(String fileName) { - Path configFile = getBootstrap().getConfigDirectory().resolve(fileName); - if (!Files.exists(configFile)) { - try { - Files.createDirectories(configFile.getParent()); - } catch (IOException e) { - // ignore - } + // hooks called during enable - try (InputStream is = getBootstrap().getResourceStream(fileName)) { - Files.copy(is, configFile); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return configFile; + protected void registerHousekeepingTasks() { + getBootstrap().getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS); + getBootstrap().getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES); } protected abstract void setupSenderFactory(); @@ -301,11 +294,33 @@ protected Path resolveConfig(String fileName) { protected abstract void setupPlatformHooks(); protected abstract AbstractEventBus provideEventBus(LuckPermsApiProvider apiProvider); protected abstract void registerApiOnPlatform(LuckPerms api); - protected abstract void registerHousekeepingTasks(); protected abstract void performFinalSetup(); + // hooks called during disable + protected void removePlatformHooks() {} + protected Path resolveConfig(String fileName) { + Path configFile = getBootstrap().getConfigDirectory().resolve(fileName); + + // if the config doesn't exist, create it based on the template in the resources dir + if (!Files.exists(configFile)) { + try { + Files.createDirectories(configFile.getParent()); + } catch (IOException e) { + // ignore + } + + try (InputStream is = getBootstrap().getResourceStream(fileName)) { + Files.copy(is, configFile); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return configFile; + } + @Override public PluginLogger getLogger() { return getBootstrap().getPluginLogger(); diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java index 718ea2b2c..bf3bde23e 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java @@ -40,8 +40,6 @@ import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin; import me.lucko.luckperms.common.sender.DummyConsoleSender; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; -import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.fabric.context.FabricContextManager; import me.lucko.luckperms.fabric.context.FabricPlayerCalculator; import me.lucko.luckperms.fabric.listeners.FabricConnectionListener; @@ -57,7 +55,6 @@ import java.util.Optional; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.stream.Stream; public class LPFabricPlugin extends AbstractLuckPermsPlugin { @@ -160,12 +157,6 @@ protected AbstractEventBus provideEventBus(LuckPermsApiProvider pr protected void registerApiOnPlatform(LuckPerms api) { } - @Override - protected void registerHousekeepingTasks() { - this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS); - this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES); - } - @Override protected void performFinalSetup() { } 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 d2c17533c..843a7d17d 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitPlugin.java @@ -39,8 +39,6 @@ import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin; import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; -import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.nukkit.calculator.NukkitCalculatorFactory; import me.lucko.luckperms.nukkit.context.NukkitContextManager; import me.lucko.luckperms.nukkit.context.NukkitPlayerCalculator; @@ -70,7 +68,6 @@ import cn.nukkit.utils.Config; import java.util.Optional; -import java.util.concurrent.TimeUnit; import java.util.stream.Stream; /** @@ -182,12 +179,6 @@ protected void registerApiOnPlatform(LuckPerms api) { this.bootstrap.getServer().getServiceManager().register(LuckPerms.class, api, this.bootstrap.getLoader(), ServicePriority.NORMAL); } - @Override - protected void registerHousekeepingTasks() { - this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS); - this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES); - } - @Override protected void performFinalSetup() { // register permissions 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 f3a7b83e0..90ed9664f 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java @@ -40,8 +40,6 @@ import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin; import me.lucko.luckperms.common.sender.DummyConsoleSender; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; -import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.sponge.calculator.SpongeCalculatorFactory; import me.lucko.luckperms.sponge.commands.SpongeParentCommand; import me.lucko.luckperms.sponge.context.SpongeContextManager; @@ -201,8 +199,7 @@ protected void registerApiOnPlatform(LuckPerms api) { @Override protected void registerHousekeepingTasks() { - this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS); - this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES); + super.registerHousekeepingTasks(); this.bootstrap.getScheduler().asyncRepeating(new ServiceCacheHousekeepingTask(this.service), 2, TimeUnit.MINUTES); } diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityPlugin.java b/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityPlugin.java index b3c5186f0..f3bb1ff8a 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityPlugin.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityPlugin.java @@ -40,8 +40,6 @@ import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin; import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; -import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.velocity.calculator.VelocityCalculatorFactory; import me.lucko.luckperms.velocity.context.VelocityContextManager; import me.lucko.luckperms.velocity.context.VelocityPlayerCalculator; @@ -55,7 +53,6 @@ import java.util.Optional; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.stream.Stream; /** @@ -158,12 +155,6 @@ protected void registerApiOnPlatform(LuckPerms api) { // Velocity doesn't have a services manager } - @Override - protected void registerHousekeepingTasks() { - this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS); - this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES); - } - @Override protected void performFinalSetup() { From 044deccd22485a3819c1893f25538cf03e4c1bef Mon Sep 17 00:00:00 2001 From: Tjeu Kayim <15987676+TjeuKayim@users.noreply.github.com> Date: Thu, 10 Jun 2021 22:12:03 +0200 Subject: [PATCH 004/314] Fabric, fix GameMode.NOT_SET removed in 1.17 (#3053) --- .../lucko/luckperms/fabric/context/FabricPlayerCalculator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java index f3a0c917f..2b1b8a095 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java @@ -75,7 +75,8 @@ public void registerListeners() { @Override public void calculate(@NonNull ServerPlayerEntity target, @NonNull ContextConsumer consumer) { GameMode mode = target.interactionManager.getGameMode(); - if (this.gamemode && mode != null && mode != GameMode.NOT_SET) { + final int GAME_MODE_NOT_SET = -1; // GameMode.NOT_SET with ID -1 was removed in 1.17 + if (this.gamemode && mode != null && mode.getId() != GAME_MODE_NOT_SET) { consumer.accept(DefaultContextKeys.GAMEMODE_KEY, GAMEMODE_NAMER.name(mode)); } From a63be102cfb05165893cabfa33581b843a75e38e Mon Sep 17 00:00:00 2001 From: Tjeu Kayim <15987676+TjeuKayim@users.noreply.github.com> Date: Thu, 10 Jun 2021 22:15:33 +0200 Subject: [PATCH 005/314] Implement command list updater for Fabric (#3060) --- .../luckperms/fabric/LPFabricPlugin.java | 5 + .../context/FabricPlayerCalculator.java | 1 + .../listeners/FabricCommandListUpdater.java | 118 ++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricCommandListUpdater.java diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java index bf3bde23e..384c8fe69 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java @@ -44,6 +44,7 @@ import me.lucko.luckperms.fabric.context.FabricPlayerCalculator; import me.lucko.luckperms.fabric.listeners.FabricConnectionListener; import me.lucko.luckperms.fabric.listeners.PermissionCheckListener; +import me.lucko.luckperms.fabric.listeners.FabricCommandListUpdater; import me.lucko.luckperms.fabric.messaging.FabricMessagingFactory; import net.fabricmc.loader.api.ModContainer; @@ -159,6 +160,10 @@ protected void registerApiOnPlatform(LuckPerms api) { @Override protected void performFinalSetup() { + // register fabric command list updater + if (getConfiguration().get(ConfigKeys.UPDATE_CLIENT_COMMAND_LIST)) { + getApiProvider().getEventBus().subscribe(new FabricCommandListUpdater(this)); + } } public FabricSenderFactory getSenderFactory() { diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java index 2b1b8a095..7214d071b 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java @@ -123,6 +123,7 @@ private static String getContextKey(Identifier key) { private void onWorldChange(ServerWorld origin, ServerWorld destination, ServerPlayerEntity player) { if (this.world) { this.plugin.getContextManager().invalidateCache(player); + this.plugin.getContextManager().signalContextUpdate(player); } } diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricCommandListUpdater.java b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricCommandListUpdater.java new file mode 100644 index 000000000..7bd512e1e --- /dev/null +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricCommandListUpdater.java @@ -0,0 +1,118 @@ +/* + * 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.fabric.listeners; + +import com.github.benmanes.caffeine.cache.LoadingCache; + +import me.lucko.luckperms.fabric.LPFabricPlugin; +import me.lucko.luckperms.common.cache.BufferedRequest; +import me.lucko.luckperms.common.event.LuckPermsEventListener; +import me.lucko.luckperms.common.util.CaffeineFactory; +import me.lucko.luckperms.common.api.implementation.ApiGroup; + +import net.luckperms.api.event.EventBus; +import net.luckperms.api.event.context.ContextUpdateEvent; +import net.luckperms.api.event.user.UserDataRecalculateEvent; +import net.luckperms.api.event.group.GroupDataRecalculateEvent; +import net.minecraft.server.network.ServerPlayerEntity; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * Calls {@link net.minecraft.server.PlayerManager#sendCommandTree()} when a players permissions change. + * + * TODO: Extract base class for this and BukkitCommandListUpdater + */ +public class FabricCommandListUpdater implements LuckPermsEventListener { + + private final LPFabricPlugin plugin; + private final LoadingCache sendingBuffers = CaffeineFactory.newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .build(SendBuffer::new); + + public FabricCommandListUpdater(LPFabricPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void bind(EventBus bus) { + bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate); + bus.subscribe(GroupDataRecalculateEvent.class, this::onGroupDataRecalculate); + bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate); + } + + private void onUserDataRecalculate(UserDataRecalculateEvent e) { + requestUpdate(e.getUser().getUniqueId()); + } + + private void onGroupDataRecalculate(GroupDataRecalculateEvent e) { + plugin.getUserManager().getAll().values().stream() + .filter(u -> u.resolveInheritanceTree(u.getQueryOptions()) + .contains(ApiGroup.cast(e.getGroup()))) + .forEach(u -> requestUpdate(u.getUniqueId())); + } + + private void onContextUpdate(ContextUpdateEvent e) { + e.getSubject(ServerPlayerEntity.class).ifPresent(p -> requestUpdate(p.getUuid())); + } + + private void requestUpdate(UUID uniqueId) { + if (!this.plugin.getBootstrap().isPlayerOnline(uniqueId)) { + return; + } + + // Buffer the request to send a commands update. + this.sendingBuffers.get(uniqueId).request(); + } + + // Called when the buffer times out. + private void sendUpdate(UUID uniqueId) { + this.plugin.getBootstrap().getScheduler().sync() + .execute(() -> this.plugin.getBootstrap().getPlayer(uniqueId) + .ifPresent(player -> this.plugin.getBootstrap().getServer() + .ifPresent(server -> { + server.getPlayerManager().sendCommandTree(player); + }) + ) + ); + } + + private final class SendBuffer extends BufferedRequest { + private final UUID uniqueId; + + SendBuffer(UUID uniqueId) { + super(500, TimeUnit.MILLISECONDS, FabricCommandListUpdater.this.plugin.getBootstrap().getScheduler()); + this.uniqueId = uniqueId; + } + + @Override + protected Void perform() { + sendUpdate(this.uniqueId); + return null; + } + } +} From 8ae70990adc016ba10a001fe6bc3e89392a6e53f Mon Sep 17 00:00:00 2001 From: Jacob C <40674666+yakovliam@users.noreply.github.com> Date: Thu, 10 Jun 2021 16:16:23 -0400 Subject: [PATCH 006/314] Generify SqlStorage shutdown exception message (#3039) --- .../luckperms/common/storage/implementation/sql/SqlStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index e453160bc..e9bd93bbb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -221,7 +221,7 @@ public void shutdown() { try { this.connectionFactory.shutdown(); } catch (Exception e) { - this.plugin.getLogger().severe("Exception whilst disabling SQLite storage", e); + this.plugin.getLogger().severe("Exception whilst disabling SQL storage", e); } } From 51202924af0c49dbaa9e0fab10b9e48922887e41 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 12 Jun 2021 17:57:43 +0100 Subject: [PATCH 007/314] Update commodore for MC 1.17 support --- .../me/lucko/luckperms/common/dependencies/Dependency.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index 12a1cb7b7..b9eb0dad9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -127,8 +127,8 @@ public enum Dependency { COMMODORE( "me{}lucko", "commodore", - "1.9", - "EhaLLqbgPnVYa61RumUc7l3r6wcGg2edDh2+PR8pvHI=", + "1.10", + "DJi8ZLaSwhoU49UBrIFzVBksaVAj8bthtVCnhuBPRz4=", Relocation.of("commodore", "me{}lucko{}commodore") ), COMMODORE_FILE( From 4598435dd4e1e3187f4ed58e770785ec08878c7d Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 13 Jun 2021 12:46:02 +0100 Subject: [PATCH 008/314] Fix extension loading causing whole plugin to error --- .../extension/SimpleExtensionManager.java | 13 ++++------ .../ReflectionClassPathAppender.java | 25 +++++++++++++++---- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/extension/SimpleExtensionManager.java b/common/src/main/java/me/lucko/luckperms/common/extension/SimpleExtensionManager.java index 05598d03a..f271e50ea 100644 --- a/common/src/main/java/me/lucko/luckperms/common/extension/SimpleExtensionManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/extension/SimpleExtensionManager.java @@ -28,6 +28,7 @@ import com.google.gson.JsonObject; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.plugin.classpath.ReflectionClassPathAppender; import me.lucko.luckperms.common.util.gson.GsonProvider; import net.luckperms.api.LuckPerms; @@ -42,8 +43,6 @@ import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -98,7 +97,7 @@ public void loadExtensions(Path directory) { if (path.getFileName().toString().endsWith(".jar")) { try { loadExtension(path); - } catch (IOException e) { + } catch (Exception e) { this.plugin.getLogger().warn("Exception loading extension from " + path, e); } } @@ -146,7 +145,7 @@ public void loadExtensions(Path directory) { if (useParentClassLoader && isJarInJar()) { try { addJarToParentClasspath(path); - } catch (Exception e) { + } catch (Throwable e) { throw new RuntimeException("Exception whilst classloading extension", e); } } else { @@ -200,16 +199,14 @@ private static boolean isJarInJar() { return thisClassLoaderName.equals("me.lucko.luckperms.common.loader.JarInJarClassLoader"); } + @Deprecated private static void addJarToParentClasspath(Path path) throws Exception { ClassLoader parentClassLoader = SimpleExtensionManager.class.getClassLoader().getParent(); if (!(parentClassLoader instanceof URLClassLoader)) { throw new RuntimeException("useParentClassLoader is true but parent is not a URLClassLoader"); } - Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - addUrlMethod.setAccessible(true); - - addUrlMethod.invoke(parentClassLoader, path.toUri().toURL()); + ReflectionClassPathAppender.addUrl(((URLClassLoader) parentClassLoader), path.toUri().toURL()); } private static final class LoadedExtension { diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java index ebb175227..2aec61edb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java @@ -43,16 +43,32 @@ public class ReflectionClassPathAppender implements ClassPathAppender { try { openUrlClassLoaderModule(); } catch (Throwable e) { - // ignore exception - will throw on Java 8 since the Module classes don't exist + // ignore } - // Get the protected 'addURL' method on URLClassLoader and set it to accessible. try { ADD_URL_METHOD = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - ADD_URL_METHOD.setAccessible(true); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } + + try { + ADD_URL_METHOD.setAccessible(true); + } catch (Throwable e) { + new RuntimeException("LuckPerms is unable to access the URLClassLoader#addURL method using reflection. \n" + + "You may be able to fix this problem by adding the following command-line argument " + + "directly after the 'java' command in your start script: \n'--add-opens java.base/java.lang=ALL-UNNAMED'", e).printStackTrace(); + } + } + + /** + * Adds the given {@link URL} to the class loader. + * + * @param classLoader the class loader + * @param url the url to add + */ + public static void addUrl(URLClassLoader classLoader, URL url) throws ReflectiveOperationException { + ADD_URL_METHOD.invoke(classLoader, url); } private final URLClassLoader classLoader; @@ -69,13 +85,12 @@ public ReflectionClassPathAppender(LuckPermsBootstrap bootstrap) throws IllegalS @Override public void addJarToClasspath(Path file) { try { - ADD_URL_METHOD.invoke(this.classLoader, file.toUri().toURL()); + addUrl(this.classLoader, file.toUri().toURL()); } catch (ReflectiveOperationException | MalformedURLException e) { throw new RuntimeException(e); } } - @SuppressWarnings("JavaReflectionMemberAccess") private static void openUrlClassLoaderModule() throws Exception { // This is effectively calling: // From 979c80d9eeabd45b107401c119f77cf0e3a3081f Mon Sep 17 00:00:00 2001 From: Luck Date: Tue, 15 Jun 2021 20:41:36 +0100 Subject: [PATCH 009/314] Clarify config comments for mongodb-connection-uri option (#3062) --- bukkit/src/main/resources/config.yml | 18 ++++++++++++++---- bungee/src/main/resources/config.yml | 18 ++++++++++++++---- fabric/src/main/resources/luckperms.conf | 18 ++++++++++++++---- nukkit/src/main/resources/config.yml | 18 ++++++++++++++---- sponge/src/main/resources/luckperms.conf | 18 ++++++++++++++---- velocity/src/main/resources/config.yml | 18 ++++++++++++++---- 6 files changed, 84 insertions(+), 24 deletions(-) diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index 7fc20df99..3dd7fa872 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -154,15 +154,25 @@ data: #verifyServerCertificate: false # The prefix for all LuckPerms SQL tables. + # + # - This only applies for remote SQL storage types (MySQL, MariaDB, etc). # - Change this if you want to use different tables for different servers. table-prefix: 'luckperms_' - # The prefix to use for all LuckPerms collections. Change this if you want to use different - # collections for different servers. The default is no prefix. + # The prefix to use for all LuckPerms MongoDB collections. + # + # - This only applies for the MongoDB storage type. + # - Change this if you want to use different collections for different servers. The default is no + # prefix. mongodb-collection-prefix: '' - # MongoDB ClientConnectionURI for use with replica sets and custom connection options - # - See https://docs.mongodb.com/manual/reference/connection-string/ + # The connection string URI to use to connect to the MongoDB instance. + # + # - When configured, this setting will override anything defined in the address, database, + # username or password fields above. + # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it + # below. + # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/ mongodb-connection-uri: '' # Define settings for a "split" storage setup. diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index 27d5655f5..1b93a0d78 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -151,15 +151,25 @@ data: #verifyServerCertificate: false # The prefix for all LuckPerms SQL tables. + # + # - This only applies for remote SQL storage types (MySQL, MariaDB, etc). # - Change this if you want to use different tables for different servers. table-prefix: 'luckperms_' - # The prefix to use for all LuckPerms collections. Change this if you want to use different - # collections for different servers. The default is no prefix. + # The prefix to use for all LuckPerms MongoDB collections. + # + # - This only applies for the MongoDB storage type. + # - Change this if you want to use different collections for different servers. The default is no + # prefix. mongodb-collection-prefix: '' - # MongoDB ClientConnectionURI for use with replica sets and custom connection options - # - See https://docs.mongodb.com/manual/reference/connection-string/ + # The connection string URI to use to connect to the MongoDB instance. + # + # - When configured, this setting will override anything defined in the address, database, + # username or password fields above. + # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it + # below. + # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/ mongodb-connection-uri: '' # Define settings for a "split" storage setup. diff --git a/fabric/src/main/resources/luckperms.conf b/fabric/src/main/resources/luckperms.conf index fe8b50d12..3b03682a7 100644 --- a/fabric/src/main/resources/luckperms.conf +++ b/fabric/src/main/resources/luckperms.conf @@ -156,15 +156,25 @@ data { } # The prefix for all LuckPerms SQL tables. + # + # - This only applies for remote SQL storage types (MySQL, MariaDB, etc). # - Change this if you want to use different tables for different servers. table-prefix = "luckperms_" - # The prefix to use for all LuckPerms collections. Change this if you want to use different - # collections for different servers. The default is no prefix. + # The prefix to use for all LuckPerms MongoDB collections. + # + # - This only applies for the MongoDB storage type. + # - Change this if you want to use different collections for different servers. The default is no + # prefix. mongodb-collection-prefix = "" - # MongoDB ClientConnectionURI for use with replica sets and custom connection options - # - See https://docs.mongodb.com/manual/reference/connection-string/ + # The connection string URI to use to connect to the MongoDB instance. + # + # - When configured, this setting will override anything defined in the address, database, + # username or password fields above. + # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it + # below. + # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/ mongodb-connection-uri = "" } diff --git a/nukkit/src/main/resources/config.yml b/nukkit/src/main/resources/config.yml index efd7686ff..574ebed59 100644 --- a/nukkit/src/main/resources/config.yml +++ b/nukkit/src/main/resources/config.yml @@ -154,15 +154,25 @@ data: #verifyServerCertificate: false # The prefix for all LuckPerms SQL tables. + # + # - This only applies for remote SQL storage types (MySQL, MariaDB, etc). # - Change this if you want to use different tables for different servers. table-prefix: 'luckperms_' - # The prefix to use for all LuckPerms collections. Change this if you want to use different - # collections for different servers. The default is no prefix. + # The prefix to use for all LuckPerms MongoDB collections. + # + # - This only applies for the MongoDB storage type. + # - Change this if you want to use different collections for different servers. The default is no + # prefix. mongodb-collection-prefix: '' - # MongoDB ClientConnectionURI for use with replica sets and custom connection options - # - See https://docs.mongodb.com/manual/reference/connection-string/ + # The connection string URI to use to connect to the MongoDB instance. + # + # - When configured, this setting will override anything defined in the address, database, + # username or password fields above. + # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it + # below. + # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/ mongodb-connection-uri: '' # Define settings for a "split" storage setup. diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 26c596ba6..764cac2f8 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -156,15 +156,25 @@ data { } # The prefix for all LuckPerms SQL tables. + # + # - This only applies for remote SQL storage types (MySQL, MariaDB, etc). # - Change this if you want to use different tables for different servers. table-prefix = "luckperms_" - # The prefix to use for all LuckPerms collections. Change this if you want to use different - # collections for different servers. The default is no prefix. + # The prefix to use for all LuckPerms MongoDB collections. + # + # - This only applies for the MongoDB storage type. + # - Change this if you want to use different collections for different servers. The default is no + # prefix. mongodb-collection-prefix = "" - # MongoDB ClientConnectionURI for use with replica sets and custom connection options - # - See https://docs.mongodb.com/manual/reference/connection-string/ + # The connection string URI to use to connect to the MongoDB instance. + # + # - When configured, this setting will override anything defined in the address, database, + # username or password fields above. + # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it + # below. + # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/ mongodb-connection-uri = "" } diff --git a/velocity/src/main/resources/config.yml b/velocity/src/main/resources/config.yml index 382476b8d..56f8799a3 100644 --- a/velocity/src/main/resources/config.yml +++ b/velocity/src/main/resources/config.yml @@ -144,15 +144,25 @@ data: #verifyServerCertificate: false # The prefix for all LuckPerms SQL tables. + # + # - This only applies for remote SQL storage types (MySQL, MariaDB, etc). # - Change this if you want to use different tables for different servers. table-prefix: 'luckperms_' - # The prefix to use for all LuckPerms collections. Change this if you want to use different - # collections for different servers. The default is no prefix. + # The prefix to use for all LuckPerms MongoDB collections. + # + # - This only applies for the MongoDB storage type. + # - Change this if you want to use different collections for different servers. The default is no + # prefix. mongodb-collection-prefix: '' - # MongoDB ClientConnectionURI for use with replica sets and custom connection options - # - See https://docs.mongodb.com/manual/reference/connection-string/ + # The connection string URI to use to connect to the MongoDB instance. + # + # - When configured, this setting will override anything defined in the address, database, + # username or password fields above. + # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it + # below. + # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/ mongodb-connection-uri: '' # Define settings for a "split" storage setup. From fb36bd0ced44be91ed3a52f6a85a362e77eda63c Mon Sep 17 00:00:00 2001 From: Emily <35617540+emilyy-dev@users.noreply.github.com> Date: Sun, 4 Jul 2021 06:00:18 -0300 Subject: [PATCH 010/314] Bulk update triggers for group related commands (#3073) --- bukkit/src/main/resources/luckperms.commodore | 8 +++- .../common/commands/group/DeleteGroup.java | 39 +++++++++++++++++- .../common/commands/group/GroupRename.java | 40 ++++++++++++++++++- .../commands/misc/BulkUpdateCommand.java | 2 +- 4 files changed, 82 insertions(+), 7 deletions(-) diff --git a/bukkit/src/main/resources/luckperms.commodore b/bukkit/src/main/resources/luckperms.commodore index b6693077f..c74b52525 100644 --- a/bukkit/src/main/resources/luckperms.commodore +++ b/bukkit/src/main/resources/luckperms.commodore @@ -62,7 +62,9 @@ luckperms { } } deletegroup { - name brigadier:string single_word; + name brigadier:string single_word { + flags brigadier:string greedy_phrase; + } } listgroups; createtrack { @@ -546,7 +548,9 @@ luckperms { context brigadier:string greedy_phrase; } rename { - newname brigadier:string single_word; + newname brigadier:string single_word { + flags brigadier:string greedy_phrase; + } } clone { newname brigadier:string single_word; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/DeleteGroup.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/DeleteGroup.java index 66628c6aa..9a59556f2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/DeleteGroup.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/DeleteGroup.java @@ -26,16 +26,28 @@ package me.lucko.luckperms.common.commands.group; import me.lucko.luckperms.common.actionlog.LoggedAction; +import me.lucko.luckperms.common.bulkupdate.BulkUpdate; +import me.lucko.luckperms.common.bulkupdate.BulkUpdateBuilder; +import me.lucko.luckperms.common.bulkupdate.DataType; +import me.lucko.luckperms.common.bulkupdate.action.DeleteAction; +import me.lucko.luckperms.common.bulkupdate.comparison.Constraint; +import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison; +import me.lucko.luckperms.common.bulkupdate.query.Query; +import me.lucko.luckperms.common.bulkupdate.query.QueryField; import me.lucko.luckperms.common.command.abstraction.SingleCommand; import me.lucko.luckperms.common.command.access.ArgumentPermissions; import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.command.spec.CommandSpec; +import me.lucko.luckperms.common.command.tabcomplete.CompletionSupplier; import me.lucko.luckperms.common.command.tabcomplete.TabCompleter; import me.lucko.luckperms.common.command.tabcomplete.TabCompletions; import me.lucko.luckperms.common.command.utils.ArgumentList; +import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.common.messaging.InternalMessagingService; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.manager.group.GroupManager; +import me.lucko.luckperms.common.node.types.Inheritance; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; @@ -44,10 +56,11 @@ import net.luckperms.api.event.cause.DeletionCause; import java.util.List; +import java.util.Optional; public class DeleteGroup extends SingleCommand { public DeleteGroup() { - super(CommandSpec.DELETE_GROUP, "DeleteGroup", CommandPermission.DELETE_GROUP, Predicates.not(1)); + super(CommandSpec.DELETE_GROUP, "DeleteGroup", CommandPermission.DELETE_GROUP, Predicates.notInRange(1, 2)); } @Override @@ -89,13 +102,35 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St .description("delete") .build().submit(plugin, sender); - plugin.getSyncTaskBuffer().request(); + if (!args.remove("--update-parent-lists")) { + plugin.getSyncTaskBuffer().request(); + } else { + // the group is now deleted, proceed to remove its representing inheritance nodes + BulkUpdate operation = BulkUpdateBuilder.create() + .trackStatistics(false) + .dataType(DataType.ALL) + .action(DeleteAction.create()) + .query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.EQUAL, Inheritance.key(groupName)))) + .build(); + plugin.getStorage().applyBulkUpdate(operation).whenCompleteAsync((v, ex) -> { + if (ex != null) { + ex.printStackTrace(); + } + + plugin.getSyncTaskBuffer().requestDirectly(); // sync regardless of failure state + Optional messagingService = plugin.getMessagingService(); + if (messagingService.isPresent() && plugin.getConfiguration().get(ConfigKeys.AUTO_PUSH_UPDATES)) { + messagingService.get().getUpdateBuffer().request(); + } + }, plugin.getBootstrap().getScheduler().async()); + } } @Override public List tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentList args) { return TabCompleter.create() .at(0, TabCompletions.groups(plugin)) + .at(1, CompletionSupplier.startsWith("--update-parent-lists")) .complete(args); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java index 11081760c..0186e3dcf 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java @@ -26,14 +26,24 @@ package me.lucko.luckperms.common.commands.group; import me.lucko.luckperms.common.actionlog.LoggedAction; +import me.lucko.luckperms.common.bulkupdate.BulkUpdate; +import me.lucko.luckperms.common.bulkupdate.BulkUpdateBuilder; +import me.lucko.luckperms.common.bulkupdate.action.UpdateAction; +import me.lucko.luckperms.common.bulkupdate.comparison.Constraint; +import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison; +import me.lucko.luckperms.common.bulkupdate.query.Query; +import me.lucko.luckperms.common.bulkupdate.query.QueryField; import me.lucko.luckperms.common.command.abstraction.ChildCommand; import me.lucko.luckperms.common.command.access.ArgumentPermissions; import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.command.spec.CommandSpec; +import me.lucko.luckperms.common.command.tabcomplete.CompletionSupplier; +import me.lucko.luckperms.common.command.tabcomplete.TabCompleter; import me.lucko.luckperms.common.command.utils.ArgumentList; import me.lucko.luckperms.common.command.utils.StorageAssistant; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; +import me.lucko.luckperms.common.node.types.Inheritance; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.storage.misc.DataConstraints; @@ -44,9 +54,11 @@ import net.luckperms.api.event.cause.DeletionCause; import net.luckperms.api.model.data.DataType; +import java.util.List; + public class GroupRename extends ChildCommand { public GroupRename() { - super(CommandSpec.GROUP_RENAME, "rename", CommandPermission.GROUP_RENAME, Predicates.not(1)); + super(CommandSpec.GROUP_RENAME, "rename", CommandPermission.GROUP_RENAME, Predicates.notInRange(1, 2)); } @Override @@ -92,6 +104,30 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Group target, Argumen .description("rename", newGroup.getName()) .build().submit(plugin, sender); - StorageAssistant.save(newGroup, sender, plugin); + if (!args.remove("--update-parent-lists")) { + StorageAssistant.save(newGroup, sender, plugin); + } else { + // the group is now renamed, proceed to update its representing inheritance nodes + BulkUpdate operation = BulkUpdateBuilder.create() + .trackStatistics(false) + .dataType(me.lucko.luckperms.common.bulkupdate.DataType.ALL) + .action(UpdateAction.of(QueryField.PERMISSION, Inheritance.key(newGroupName))) + .query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.EQUAL, Inheritance.key(target.getName())))) + .build(); + plugin.getStorage().applyBulkUpdate(operation).whenCompleteAsync((v, ex) -> { + plugin.getSyncTaskBuffer().requestDirectly(); + if (ex != null) { + ex.printStackTrace(); + } + }, plugin.getBootstrap().getScheduler().async()) + .thenRunAsync(() -> StorageAssistant.save(newGroup, sender, plugin)); + } + } + + @Override + public List tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentList args) { + return TabCompleter.create() + .at(1, CompletionSupplier.startsWith("--update-parent-lists")) + .complete(args); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java index 7c5091590..9f6d9d78d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java @@ -87,7 +87,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St BulkUpdateBuilder bulkUpdateBuilder = BulkUpdateBuilder.create(); - bulkUpdateBuilder.trackStatistics(!args.remove("--silent")); + bulkUpdateBuilder.trackStatistics(!args.remove("-s")); try { bulkUpdateBuilder.dataType(DataType.valueOf(args.remove(0).toUpperCase())); From 563b5be9b2bac8a82aefe8b6719c10bb82f8a28b Mon Sep 17 00:00:00 2001 From: Emily <35617540+emilyy-dev@users.noreply.github.com> Date: Sun, 4 Jul 2021 06:01:47 -0300 Subject: [PATCH 011/314] Implement enable-ops and auto-op on Fabric (#3069) --- .../luckperms/fabric/LPFabricPlugin.java | 27 +++++- .../event/PreExecuteCommandCallback.java | 43 ++++++++ .../listeners/FabricAutoOpListener.java | 97 +++++++++++++++++++ .../listeners/FabricOtherListeners.java | 66 +++++++++++++ .../fabric/mixin/CommandManagerMixin.java | 45 +++++++++ fabric/src/main/resources/luckperms.conf | 28 +++++- .../src/main/resources/luckperms.mixins.json | 5 +- 7 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 fabric/src/main/java/me/lucko/luckperms/fabric/event/PreExecuteCommandCallback.java create mode 100644 fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricAutoOpListener.java create mode 100644 fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricOtherListeners.java create mode 100644 fabric/src/main/java/me/lucko/luckperms/fabric/mixin/CommandManagerMixin.java diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java index 384c8fe69..e70a1b9b5 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java @@ -42,18 +42,23 @@ import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.fabric.context.FabricContextManager; import me.lucko.luckperms.fabric.context.FabricPlayerCalculator; +import me.lucko.luckperms.fabric.listeners.FabricAutoOpListener; +import me.lucko.luckperms.fabric.listeners.FabricCommandListUpdater; import me.lucko.luckperms.fabric.listeners.FabricConnectionListener; +import me.lucko.luckperms.fabric.listeners.FabricOtherListeners; import me.lucko.luckperms.fabric.listeners.PermissionCheckListener; -import me.lucko.luckperms.fabric.listeners.FabricCommandListUpdater; import me.lucko.luckperms.fabric.messaging.FabricMessagingFactory; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.loader.api.ModContainer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; import net.luckperms.api.LuckPerms; import net.luckperms.api.query.QueryOptions; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.OperatorList; +import java.io.IOException; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; @@ -88,6 +93,8 @@ protected void registerFabricListeners() { // Command registration also need to occur early, and will persist across game states as well. this.commandManager = new FabricCommandExecutor(this); this.commandManager.register(); + + new FabricOtherListeners(this).registerListeners(); } @Override @@ -160,6 +167,24 @@ protected void registerApiOnPlatform(LuckPerms api) { @Override protected void performFinalSetup() { + // remove all operators on startup if they're disabled + if (!getConfiguration().get(ConfigKeys.OPS_ENABLED)) { + ServerLifecycleEvents.SERVER_STARTED.register(server -> { + OperatorList operatorList = server.getPlayerManager().getOpList(); + operatorList.values().clear(); + try { + operatorList.save(); + } catch (IOException exception) { + exception.printStackTrace(); + } + }); + } + + // register autoop listener + if (getConfiguration().get(ConfigKeys.AUTO_OP)) { + getApiProvider().getEventBus().subscribe(new FabricAutoOpListener(this)); + } + // register fabric command list updater if (getConfiguration().get(ConfigKeys.UPDATE_CLIENT_COMMAND_LIST)) { getApiProvider().getEventBus().subscribe(new FabricCommandListUpdater(this)); diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/event/PreExecuteCommandCallback.java b/fabric/src/main/java/me/lucko/luckperms/fabric/event/PreExecuteCommandCallback.java new file mode 100644 index 000000000..ccb516d84 --- /dev/null +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/event/PreExecuteCommandCallback.java @@ -0,0 +1,43 @@ +/* + * 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.fabric.event; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.server.command.ServerCommandSource; + +public interface PreExecuteCommandCallback { + Event EVENT = EventFactory.createArrayBacked(PreExecuteCommandCallback.class, listeners -> (source, input) -> { + for (PreExecuteCommandCallback listener : listeners) { + if (!listener.onPreExecuteCommand(source, input)) { + return false; + } + } + return true; + }); + + boolean onPreExecuteCommand(ServerCommandSource source, String input); +} diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricAutoOpListener.java b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricAutoOpListener.java new file mode 100644 index 000000000..90ff6ffdf --- /dev/null +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricAutoOpListener.java @@ -0,0 +1,97 @@ +/* + * 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.fabric.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.fabric.LPFabricPlugin; + +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 net.minecraft.server.network.ServerPlayerEntity; + +import java.util.Map; + +public class FabricAutoOpListener implements LuckPermsEventListener { + private static final String NODE = "luckperms.autoop"; + + private final LPFabricPlugin plugin; + + public FabricAutoOpListener(LPFabricPlugin 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(ServerPlayerEntity.class).ifPresent(p -> refreshAutoOp(p, true)); + } + + private void refreshAutoOp(ServerPlayerEntity player, boolean callerIsSync) { + if (!callerIsSync && !this.plugin.getBootstrap().getServer().isPresent()) { + return; + } + + User user = this.plugin.getUserManager().getIfLoaded(player.getUuid()); + + 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) { + setOp(player, value); + } else { + this.plugin.getBootstrap().getScheduler().executeSync(() -> setOp(player, value)); + } + } + + private void setOp(ServerPlayerEntity player, boolean value) { + this.plugin.getBootstrap().getServer().ifPresent(server -> { + if (value) { + server.getPlayerManager().addToOperators(player.getGameProfile()); + } else { + server.getPlayerManager().removeFromOperators(player.getGameProfile()); + } + }); + } +} diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricOtherListeners.java b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricOtherListeners.java new file mode 100644 index 000000000..96f854bae --- /dev/null +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricOtherListeners.java @@ -0,0 +1,66 @@ +/* + * 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.fabric.listeners; + +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.fabric.LPFabricPlugin; +import me.lucko.luckperms.fabric.event.PreExecuteCommandCallback; + +import net.minecraft.server.command.ServerCommandSource; + +import java.util.regex.Pattern; + +public class FabricOtherListeners { + private static final Pattern OP_COMMAND_PATTERN = Pattern.compile("^/?(deop|op)( .*)?$"); + + private LPFabricPlugin plugin; + + public FabricOtherListeners(LPFabricPlugin plugin) { + this.plugin = plugin; + } + + public void registerListeners() { + PreExecuteCommandCallback.EVENT.register(this::onPreExecuteCommand); + } + + private boolean onPreExecuteCommand(ServerCommandSource source, String input) { + if (input.isEmpty()) { + return true; + } + + if (this.plugin.getConfiguration().get(ConfigKeys.OPS_ENABLED)) { + return true; + } + + if (OP_COMMAND_PATTERN.matcher(input).matches()) { + Message.OP_DISABLED.send(this.plugin.getSenderFactory().wrap(source)); + return false; + } + + return true; + } +} diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/CommandManagerMixin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/CommandManagerMixin.java new file mode 100644 index 000000000..4ff389685 --- /dev/null +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/CommandManagerMixin.java @@ -0,0 +1,45 @@ +/* + * 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.fabric.mixin; + +import me.lucko.luckperms.fabric.event.PreExecuteCommandCallback; + +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(CommandManager.class) +public class CommandManagerMixin { + @Inject(at = @At("HEAD"), method = "execute", cancellable = true) + private void commandExecuteCallback(ServerCommandSource source, String input, CallbackInfoReturnable info) { + if (!PreExecuteCommandCallback.EVENT.invoker().onPreExecuteCommand(source, input)) { + info.setReturnValue(0); + } + } +} diff --git a/fabric/src/main/resources/luckperms.conf b/fabric/src/main/resources/luckperms.conf index 3b03682a7..f8769ebc8 100644 --- a/fabric/src/main/resources/luckperms.conf +++ b/fabric/src/main/resources/luckperms.conf @@ -513,7 +513,7 @@ apply-wildcards = true # - That being: If a user has been granted "example", then the player should have also be # automatically granted "example.function", "example.another", "example.deeper.nesting", # and so on. -apply-sponge-implicit-wildcards=true +apply-sponge-implicit-wildcards = true # If the plugin should parse regex permissions. # @@ -565,6 +565,29 @@ group-weight { # | | # # +----------------------------------------------------------------------------------------------+ # +# +----------------------------------------------------------------------------------------------+ # +# | Server Operator (OP) settings | # +# +----------------------------------------------------------------------------------------------+ # + +# Controls whether server operators should exist at all. +# +# - When set to 'false', all players will be de-opped, and the /op and /deop commands will be +# disabled. +enable-ops = true + +# Enables or disables a special permission based system in LuckPerms for controlling OP status. +# +# - If set to true, any user with the permission "luckperms.autoop" will automatically be granted +# server operator status. This permission can be inherited, or set on specific servers/worlds, +# temporarily, etc. +# - Additionally, setting this to true will force the "enable-ops" option above to false. All users +# will be de-opped unless they have the permission node, and the op/deop commands will be +# disabled. +# - It is recommended that you use this option instead of assigning a single '*' permission. +# - However, on Fabric this setting can be used as a "pseudo" root wildcard, as many mods support +# the operator system over permissions. +auto-op = false + # +----------------------------------------------------------------------------------------------+ # # | Miscellaneous (and rarely used) settings | # # +----------------------------------------------------------------------------------------------+ # @@ -593,6 +616,9 @@ skip-bulkupdate-confirmation = false # - When this happens, the plugin will set their primary group back to default. prevent-primary-group-removal = false +# If LuckPerms should update the list of commands sent to the client when permissions are changed. +update-client-command-list = true + # If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands. # See here for more info: https://minecraft.gamepedia.com/Commands#Target_selectors resolve-command-selectors = false diff --git a/fabric/src/main/resources/luckperms.mixins.json b/fabric/src/main/resources/luckperms.mixins.json index b83dfbca7..392d4dac7 100644 --- a/fabric/src/main/resources/luckperms.mixins.json +++ b/fabric/src/main/resources/luckperms.mixins.json @@ -5,11 +5,12 @@ "mixins": [ "ClientSettingsC2SPacketAccessor", "ServerLoginNetworkHandlerAccessor", - "ServerPlayerEntityMixin" + "ServerPlayerEntityMixin", + "CommandManagerMixin" ], "client": [ ], "injectors": { "defaultRequire": 1 } -} \ No newline at end of file +} From cfb298f69baf84f627b5881f8e65183fda1e0f17 Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 8 Jul 2021 22:53:43 +0100 Subject: [PATCH 012/314] Use different classifier for dependency files with Bukkit-Legacy remappings --- .../lucko/luckperms/common/dependencies/Dependency.java | 9 +++++++-- .../luckperms/common/dependencies/DependencyManager.java | 4 ++-- .../common/dependencies/DependencyRegistry.java | 7 ++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index b9eb0dad9..18d645154 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -313,8 +313,13 @@ private static String rewriteEscaping(String s) { return s.replace("{}", "."); } - public String getFileName() { - return name().toLowerCase().replace('_', '-') + "-" + this.version; + public String getFileName(String classifier) { + String name = name().toLowerCase().replace('_', '-'); + String extra = classifier == null || classifier.isEmpty() + ? "" + : "-" + classifier; + + return name + "-" + this.version + extra + ".jar"; } String getMavenRepoPath() { diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java index 7cbee16a8..696ec8094 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java @@ -155,7 +155,7 @@ private void loadDependency(Dependency dependency) throws Exception { } private Path downloadDependency(Dependency dependency) throws DependencyDownloadException { - Path file = this.cacheDirectory.resolve(dependency.getFileName() + ".jar"); + Path file = this.cacheDirectory.resolve(dependency.getFileName(null)); // if the file already exists, don't attempt to re-download it. if (Files.exists(file)) { @@ -185,7 +185,7 @@ private Path remapDependency(Dependency dependency, Path normalFile) throws Exce return normalFile; } - Path remappedFile = this.cacheDirectory.resolve(dependency.getFileName() + "-remapped.jar"); + Path remappedFile = this.cacheDirectory.resolve(dependency.getFileName(DependencyRegistry.isGsonRelocated() ? "remapped-legacy" : "remapped")); // if the remapped source exists already, just use that. if (Files.exists(remappedFile)) { diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java index 3f6698b30..0be355473 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java @@ -99,7 +99,7 @@ public void applyRelocationSettings(Dependency dependency, List relo Platform.Type type = this.plugin.getBootstrap().getType(); // support for LuckPerms legacy (bukkit 1.7.10) - if (!RelocationHandler.DEPENDENCIES.contains(dependency) && JsonElement.class.getName().startsWith("me.lucko")) { + if (!RelocationHandler.DEPENDENCIES.contains(dependency) && isGsonRelocated()) { relocations.add(Relocation.of("guava", "com{}google{}common")); relocations.add(Relocation.of("gson", "com{}google{}gson")); } @@ -125,6 +125,11 @@ public boolean shouldAutoLoad(Dependency dependency) { } } + @SuppressWarnings("ConstantConditions") + public static boolean isGsonRelocated() { + return JsonElement.class.getName().startsWith("me.lucko"); + } + private static boolean classExists(String className) { try { Class.forName(className); From 5a377e713eb51d118701789cec56a37746d66dbf Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 18 Jul 2021 20:35:31 +0100 Subject: [PATCH 013/314] Fix NPE from null primary group in Vault hook (#3103) --- .../lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java index abb24d214..d9354d26b 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java @@ -254,6 +254,9 @@ public String userGetPrimaryGroup(String world, UUID uuid) { QueryOptions queryOptions = getQueryOptions(uuid, world); MetaCache metaData = user.getCachedData().getMetaData(queryOptions); String value = metaData.getPrimaryGroup(MetaCheckEvent.Origin.THIRD_PARTY_API); + if (value == null) { + return null; + } Group group = getGroup(value); return group != null ? groupName(group) : value; From 5ea2aee2f49a317e8b6aa242ded2fe81384343ed Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 18 Jul 2021 20:38:06 +0100 Subject: [PATCH 014/314] More helpful error message when user is null in API PlayerAdapter (#3104) --- .../luckperms/common/api/implementation/ApiPlayerAdapter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlayerAdapter.java b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlayerAdapter.java index bb645f9a6..f9d4a62ab 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlayerAdapter.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlayerAdapter.java @@ -57,7 +57,9 @@ private P checkType(P player) { public @NonNull User getUser(@NonNull P player) { Objects.requireNonNull(player, "player"); me.lucko.luckperms.common.model.User user = this.userManager.getIfLoaded(this.contextManager.getUniqueId(checkType(player))); - Objects.requireNonNull(user, "user"); + if (user == null) { + throw new IllegalStateException("Unable to get a user for " + player); + } return user.getApiProxy(); } From 26de6d3a78608d0115549f2d6f2fcf3cb64195fe Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 9 Jul 2021 14:31:52 +0100 Subject: [PATCH 015/314] Update Velocity to 3.0.0, use a Continuation for permissions setup event --- velocity/build.gradle | 4 +- .../listeners/VelocityConnectionListener.java | 53 ++++++++++--------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/velocity/build.gradle b/velocity/build.gradle index 652bcf608..17ebab6e2 100644 --- a/velocity/build.gradle +++ b/velocity/build.gradle @@ -10,8 +10,8 @@ repositories { dependencies { compile project(':common') - compileOnly 'com.velocitypowered:velocity-api:1.1.5' - annotationProcessor 'com.velocitypowered:velocity-api:1.1.5' + compileOnly 'com.velocitypowered:velocity-api:3.0.0' + annotationProcessor 'com.velocitypowered:velocity-api:3.0.0' } blossom { diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/VelocityConnectionListener.java b/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/VelocityConnectionListener.java index eed927954..5ca621185 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/VelocityConnectionListener.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/VelocityConnectionListener.java @@ -25,6 +25,7 @@ package me.lucko.luckperms.velocity.listeners; +import com.velocitypowered.api.event.Continuation; import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.connection.DisconnectEvent; @@ -58,7 +59,7 @@ public VelocityConnectionListener(LPVelocityPlugin plugin) { } @Subscribe - public void onPlayerPermissionsSetup(PermissionsSetupEvent e) { + public void onPlayerPermissionsSetup(PermissionsSetupEvent e, Continuation continuation) { /* Called when the player first attempts a connection with the server. The PermissionsSetupEvent is called for players just before the Login event @@ -76,30 +77,34 @@ public void onPlayerPermissionsSetup(PermissionsSetupEvent e) { this.plugin.getLogger().info("Processing pre-login for " + p.getUniqueId() + " - " + p.getUsername()); } - /* Actually process the login for the connection. - We do this here to delay the login until the data is ready. - If the login gets cancelled later on, then this will be cleaned up. - - This includes: - - loading uuid data - - loading permissions - - creating a user instance in the UserManager for this connection. - - setting up cached data. */ - try { - User user = loadUser(p.getUniqueId(), p.getUsername()); - recordConnection(p.getUniqueId()); - e.setProvider(new PlayerPermissionProvider(p, user, this.plugin.getContextManager().getCacheFor(p))); - this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(p.getUniqueId(), p.getUsername(), user); - } catch (Exception ex) { - this.plugin.getLogger().severe("Exception occurred whilst loading data for " + p.getUniqueId() + " - " + p.getUsername(), ex); - - // there was some error loading - if (this.plugin.getConfiguration().get(ConfigKeys.CANCEL_FAILED_LOGINS)) { - // cancel the login attempt - this.deniedLogin.add(p.getUniqueId()); + this.plugin.getBootstrap().getScheduler().executeAsync(() -> { + /* Actually process the login for the connection. + We do this here to delay the login until the data is ready. + If the login gets cancelled later on, then this will be cleaned up. + + This includes: + - loading uuid data + - loading permissions + - creating a user instance in the UserManager for this connection. + - setting up cached data. */ + try { + User user = loadUser(p.getUniqueId(), p.getUsername()); + recordConnection(p.getUniqueId()); + e.setProvider(new PlayerPermissionProvider(p, user, this.plugin.getContextManager().getCacheFor(p))); + this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(p.getUniqueId(), p.getUsername(), user); + } catch (Exception ex) { + this.plugin.getLogger().severe("Exception occurred whilst loading data for " + p.getUniqueId() + " - " + p.getUsername(), ex); + + // there was some error loading + if (this.plugin.getConfiguration().get(ConfigKeys.CANCEL_FAILED_LOGINS)) { + // cancel the login attempt + this.deniedLogin.add(p.getUniqueId()); + } + this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(p.getUniqueId(), p.getUsername(), null); + } finally { + continuation.resume(); } - this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(p.getUniqueId(), p.getUsername(), null); - } + }); } @Subscribe(order = PostOrder.FIRST) From 433272518f8a35d3ce54e865c46b55e030b7e3c0 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 31 Jul 2021 08:29:32 +0100 Subject: [PATCH 016/314] Fix bad Continuation usage in Velocity listener --- .../luckperms/velocity/listeners/VelocityConnectionListener.java | 1 + 1 file changed, 1 insertion(+) diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/VelocityConnectionListener.java b/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/VelocityConnectionListener.java index 5ca621185..02028f794 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/VelocityConnectionListener.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/VelocityConnectionListener.java @@ -68,6 +68,7 @@ public void onPlayerPermissionsSetup(PermissionsSetupEvent e, Continuation conti Offline mode. */ if (!(e.getSubject() instanceof Player)) { + continuation.resume(); return; } From 0fa309524194e467cdc97b5c72743e5338e457c5 Mon Sep 17 00:00:00 2001 From: Emily <35617540+emilyy-dev@users.noreply.github.com> Date: Fri, 6 Aug 2021 05:59:38 -0300 Subject: [PATCH 017/314] Save renamed group before syncing with storage (#3129) --- .../command/utils/StorageAssistant.java | 42 +++++++++++-------- .../common/commands/group/GroupRename.java | 40 ++++++++++-------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/command/utils/StorageAssistant.java b/common/src/main/java/me/lucko/luckperms/common/command/utils/StorageAssistant.java index 7cc180d56..8e4bdbba1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/utils/StorageAssistant.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/utils/StorageAssistant.java @@ -37,6 +37,7 @@ import me.lucko.luckperms.common.sender.Sender; import java.util.Optional; +import java.util.concurrent.CompletableFuture; /** * Utility methods for saving users, groups and tracks. @@ -88,40 +89,28 @@ public static void save(User user, Sender sender, LuckPermsPlugin plugin) { } } - public static void save(Group group, Sender sender, LuckPermsPlugin plugin) { + public static CompletableFuture save(Group group, Sender sender, LuckPermsPlugin plugin) { try { plugin.getStorage().saveGroup(group).get(); } catch (Exception e) { plugin.getLogger().warn("Error whilst saving group", e); Message.GROUP_SAVE_ERROR.send(sender, group); - return; + return failedFuture(e); } - plugin.getGroupManager().invalidateAllGroupCaches(); - plugin.getUserManager().invalidateAllUserCaches(); - - Optional messagingService = plugin.getMessagingService(); - if (messagingService.isPresent() && plugin.getConfiguration().get(ConfigKeys.AUTO_PUSH_UPDATES)) { - messagingService.get().getUpdateBuffer().request(); - } + return invalidateCachesAndPushUpdates(plugin); } - public static void save(Track track, Sender sender, LuckPermsPlugin plugin) { + public static CompletableFuture save(Track track, Sender sender, LuckPermsPlugin plugin) { try { plugin.getStorage().saveTrack(track).get(); } catch (Exception e) { plugin.getLogger().warn("Error whilst saving track", e); Message.TRACK_SAVE_ERROR.send(sender, track.getName()); - return; + return failedFuture(e); } - plugin.getGroupManager().invalidateAllGroupCaches(); - plugin.getUserManager().invalidateAllUserCaches(); - - Optional messagingService = plugin.getMessagingService(); - if (messagingService.isPresent() && plugin.getConfiguration().get(ConfigKeys.AUTO_PUSH_UPDATES)) { - messagingService.get().getUpdateBuffer().request(); - } + return invalidateCachesAndPushUpdates(plugin); } public static void save(PermissionHolder holder, Sender sender, LuckPermsPlugin plugin) { @@ -136,4 +125,21 @@ public static void save(PermissionHolder holder, Sender sender, LuckPermsPlugin } } + public static CompletableFuture invalidateCachesAndPushUpdates(LuckPermsPlugin plugin) { + plugin.getGroupManager().invalidateAllGroupCaches(); + plugin.getUserManager().invalidateAllUserCaches(); + + Optional messagingService = plugin.getMessagingService(); + if (messagingService.isPresent() && plugin.getConfiguration().get(ConfigKeys.AUTO_PUSH_UPDATES)) { + return messagingService.get().getUpdateBuffer().request(); + } else { + return CompletableFuture.completedFuture(null); + } + } + + private static CompletableFuture failedFuture(Throwable ex) { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(ex); + return future; + } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java index 0186e3dcf..23c625fce 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java @@ -55,6 +55,7 @@ import net.luckperms.api.model.data.DataType; import java.util.List; +import java.util.concurrent.CompletableFuture; public class GroupRename extends ChildCommand { public GroupRename() { @@ -104,24 +105,27 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Group target, Argumen .description("rename", newGroup.getName()) .build().submit(plugin, sender); - if (!args.remove("--update-parent-lists")) { - StorageAssistant.save(newGroup, sender, plugin); - } else { - // the group is now renamed, proceed to update its representing inheritance nodes - BulkUpdate operation = BulkUpdateBuilder.create() - .trackStatistics(false) - .dataType(me.lucko.luckperms.common.bulkupdate.DataType.ALL) - .action(UpdateAction.of(QueryField.PERMISSION, Inheritance.key(newGroupName))) - .query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.EQUAL, Inheritance.key(target.getName())))) - .build(); - plugin.getStorage().applyBulkUpdate(operation).whenCompleteAsync((v, ex) -> { - plugin.getSyncTaskBuffer().requestDirectly(); - if (ex != null) { - ex.printStackTrace(); - } - }, plugin.getBootstrap().getScheduler().async()) - .thenRunAsync(() -> StorageAssistant.save(newGroup, sender, plugin)); - } + StorageAssistant.save(newGroup, sender, plugin) + .thenCompose((v) -> { + if (args.remove("--update-parent-lists")) { + // the group is now renamed, proceed to update its representing inheritance nodes + BulkUpdate operation = BulkUpdateBuilder.create() + .trackStatistics(false) + .dataType(me.lucko.luckperms.common.bulkupdate.DataType.ALL) + .action(UpdateAction.of(QueryField.PERMISSION, Inheritance.key(newGroupName))) + .query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.EQUAL, Inheritance.key(target.getName())))) + .build(); + return plugin.getStorage().applyBulkUpdate(operation); + } else { + return CompletableFuture.completedFuture(v); + } + }).whenCompleteAsync((v, ex) -> { + if (ex != null) { + ex.printStackTrace(); + } + + plugin.getSyncTaskBuffer().requestDirectly(); + }, plugin.getBootstrap().getScheduler().async()); } @Override From b3ebf202fd40762be6749791dd4d9ead72c9cb59 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 6 Aug 2021 14:35:15 +0100 Subject: [PATCH 018/314] Update adventure-platform for 1.17 (#3127) --- bukkit/build.gradle | 2 +- bungee/build.gradle | 2 +- common/build.gradle | 8 ++++---- .../common/dependencies/Dependency.java | 16 ++++++++-------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 1abccbb1c..458e2a71c 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -12,7 +12,7 @@ dependencies { compileOnly project(':common:loader-utils') compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT' - compileOnly('me.lucko:adventure-platform-bukkit:4.7.0') { + compileOnly('me.lucko:adventure-platform-bukkit:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') diff --git a/bungee/build.gradle b/bungee/build.gradle index 685657a5a..58bd22eba 100644 --- a/bungee/build.gradle +++ b/bungee/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly project(':common:loader-utils') compileOnly 'net.md-5:bungeecord-api:1.15-SNAPSHOT' - compileOnly('me.lucko:adventure-platform-bungeecord:4.7.0') { + compileOnly('me.lucko:adventure-platform-bungeecord:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') diff --git a/common/build.gradle b/common/build.gradle index 14f2579d6..2e83a49bb 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -16,24 +16,24 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' compileOnly 'org.apache.logging.log4j:log4j-api:2.14.0' - compile('net.kyori:adventure-api:4.7.0') { + compile('net.kyori:adventure-api:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'checker-qual') exclude(module: 'annotations') } - compile('net.kyori:adventure-text-serializer-gson:4.7.0') { + compile('net.kyori:adventure-text-serializer-gson:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'gson') } - compile('net.kyori:adventure-text-serializer-legacy:4.7.0') { + compile('net.kyori:adventure-text-serializer-legacy:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } - compile('net.kyori:adventure-text-serializer-plain:4.7.0') { + compile('net.kyori:adventure-text-serializer-plain:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index 18d645154..9c26c5be5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -63,29 +63,29 @@ public enum Dependency { ADVENTURE( "me{}lucko", "adventure-api", - "4.7.1", - "Kp0YN1he11ykhB9vSnUVHRq4/pTuOon+XuklDsSHsQw=", + "4.8.1", + "S0Txix9BuhCD8pztmpDvbWqgKflNNqO/xBdXoNxrJSU=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM( "me{}lucko", "adventure-platform-api", - "4.7.0", - "CyYWxQuoN4vHte/5HuFDZEqrBGMi9Vv7uH3toJW1Z5Y=", + "4.8.1", + "1LAWziMjueMDuNDNv1QqqJRt40inA9H7b3rI5cPc+ZU=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUKKIT( "me{}lucko", "adventure-platform-bukkit", - "4.7.0", - "9H5MHWbJlZAHZR5zqqjW3QBU0GhJ1l9KgLGE4mKHDe8=", + "4.8.1", + "0YYq9QFRJBaPCOS4CVTCkT6BDAsnK38FaRPl6oc4sU8=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUNGEECORD( "me{}lucko", "adventure-platform-bungeecord", - "4.7.0", - "puM9PtfRzhp1Gq+ZxRAhVZqDblN1P2bb8FUnhLkMVsA=", + "4.8.1", + "hmCpfgWQHMpDfuOji+TVp2GtKxSqgOLzy7bOFb32i7g=", Relocation.of("adventure", "net{}kyori{}adventure") ), EVENT( From ee6aa47d9989efa0aa0ee5ff76d7e5af380c3dd4 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 14 Aug 2021 09:08:14 +0100 Subject: [PATCH 019/314] Listen for ServerPostConnectEvent in Velocity context listener (#3133) --- .../luckperms/velocity/context/VelocityPlayerCalculator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java b/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java index 6aa0d57f6..7153e8c71 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java @@ -27,7 +27,7 @@ import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.Subscribe; -import com.velocitypowered.api.event.player.ServerConnectedEvent; +import com.velocitypowered.api.event.player.ServerPostConnectEvent; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; @@ -71,7 +71,7 @@ public void calculate(@NonNull Player subject, @NonNull ContextConsumer consumer } @Subscribe(order = PostOrder.FIRST) - public void onServerConnect(ServerConnectedEvent e) { + public void onServerConnect(ServerPostConnectEvent e) { this.plugin.getContextManager().signalContextUpdate(e.getPlayer()); } } From b232cb94b4ef79de0cb62756114472cc14ced8d9 Mon Sep 17 00:00:00 2001 From: Luck Date: Tue, 17 Aug 2021 21:29:10 +0100 Subject: [PATCH 020/314] ReflectionClassPathAppender on Java 16+ --- .../extension/SimpleExtensionManager.java | 4 +- .../ReflectionClassPathAppender.java | 66 +----- .../classpath/URLClassLoaderAccess.java | 188 ++++++++++++++++++ 3 files changed, 194 insertions(+), 64 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/plugin/classpath/URLClassLoaderAccess.java diff --git a/common/src/main/java/me/lucko/luckperms/common/extension/SimpleExtensionManager.java b/common/src/main/java/me/lucko/luckperms/common/extension/SimpleExtensionManager.java index f271e50ea..9a0143c9b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/extension/SimpleExtensionManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/extension/SimpleExtensionManager.java @@ -28,7 +28,7 @@ import com.google.gson.JsonObject; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import me.lucko.luckperms.common.plugin.classpath.ReflectionClassPathAppender; +import me.lucko.luckperms.common.plugin.classpath.URLClassLoaderAccess; import me.lucko.luckperms.common.util.gson.GsonProvider; import net.luckperms.api.LuckPerms; @@ -206,7 +206,7 @@ private static void addJarToParentClasspath(Path path) throws Exception { throw new RuntimeException("useParentClassLoader is true but parent is not a URLClassLoader"); } - ReflectionClassPathAppender.addUrl(((URLClassLoader) parentClassLoader), path.toUri().toURL()); + URLClassLoaderAccess.create(((URLClassLoader) parentClassLoader)).addURL(path.toUri().toURL()); } private static final class LoadedExtension { diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java index 2aec61edb..11cdb8f97 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java @@ -27,56 +27,17 @@ import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; -import java.lang.reflect.Method; import java.net.MalformedURLException; -import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; -@Deprecated // TODO: no longer works on Java 16 - Sponge needs to switch to JarInJar or find an API to add to classpath at runtime public class ReflectionClassPathAppender implements ClassPathAppender { - private static final Method ADD_URL_METHOD; - - static { - // If on Java 9+, open the URLClassLoader module to this module - // so we can access its API via reflection without producing a warning. - try { - openUrlClassLoaderModule(); - } catch (Throwable e) { - // ignore - } - - try { - ADD_URL_METHOD = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - - try { - ADD_URL_METHOD.setAccessible(true); - } catch (Throwable e) { - new RuntimeException("LuckPerms is unable to access the URLClassLoader#addURL method using reflection. \n" + - "You may be able to fix this problem by adding the following command-line argument " + - "directly after the 'java' command in your start script: \n'--add-opens java.base/java.lang=ALL-UNNAMED'", e).printStackTrace(); - } - } - - /** - * Adds the given {@link URL} to the class loader. - * - * @param classLoader the class loader - * @param url the url to add - */ - public static void addUrl(URLClassLoader classLoader, URL url) throws ReflectiveOperationException { - ADD_URL_METHOD.invoke(classLoader, url); - } - - private final URLClassLoader classLoader; + private final URLClassLoaderAccess classLoaderAccess; public ReflectionClassPathAppender(LuckPermsBootstrap bootstrap) throws IllegalStateException { ClassLoader classLoader = bootstrap.getClass().getClassLoader(); if (classLoader instanceof URLClassLoader) { - this.classLoader = (URLClassLoader) classLoader; + this.classLoaderAccess = URLClassLoaderAccess.create((URLClassLoader) classLoader); } else { throw new IllegalStateException("ClassLoader is not instance of URLClassLoader"); } @@ -85,29 +46,10 @@ public ReflectionClassPathAppender(LuckPermsBootstrap bootstrap) throws IllegalS @Override public void addJarToClasspath(Path file) { try { - addUrl(this.classLoader, file.toUri().toURL()); - } catch (ReflectiveOperationException | MalformedURLException e) { + this.classLoaderAccess.addURL(file.toUri().toURL()); + } catch (MalformedURLException e) { throw new RuntimeException(e); } } - private static void openUrlClassLoaderModule() throws Exception { - // This is effectively calling: - // - // URLClassLoader.class.getModule().addOpens( - // URLClassLoader.class.getPackageName(), - // ReflectionClassLoader.class.getModule() - // ); - // - // We use reflection since we build against Java 8. - - Class moduleClass = Class.forName("java.lang.Module"); - Method getModuleMethod = Class.class.getMethod("getModule"); - Method addOpensMethod = moduleClass.getMethod("addOpens", String.class, moduleClass); - - Object urlClassLoaderModule = getModuleMethod.invoke(URLClassLoader.class); - Object thisModule = getModuleMethod.invoke(ReflectionClassPathAppender.class); - - addOpensMethod.invoke(urlClassLoaderModule, URLClassLoader.class.getPackage().getName(), thisModule); - } } diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/URLClassLoaderAccess.java b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/URLClassLoaderAccess.java new file mode 100644 index 000000000..6cdc7a7d0 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/URLClassLoaderAccess.java @@ -0,0 +1,188 @@ +/* + * 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.plugin.classpath; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collection; + +/** + * Provides access to {@link URLClassLoader}#addURL. + */ +public abstract class URLClassLoaderAccess { + + /** + * Creates a {@link URLClassLoaderAccess} for the given class loader. + * + * @param classLoader the class loader + * @return the access object + */ + public static URLClassLoaderAccess create(URLClassLoader classLoader) { + if (Reflection.isSupported()) { + return new Reflection(classLoader); + } else if (Unsafe.isSupported()) { + return new Unsafe(classLoader); + } else { + return Noop.INSTANCE; + } + } + + private final URLClassLoader classLoader; + + protected URLClassLoaderAccess(URLClassLoader classLoader) { + this.classLoader = classLoader; + } + + + /** + * Adds the given URL to the class loader. + * + * @param url the URL to add + */ + public abstract void addURL(@NonNull URL url); + + private static void throwError(Throwable cause) throws UnsupportedOperationException { + throw new UnsupportedOperationException("LuckPerms is unable to inject into the plugin URLClassLoader.\n" + + "You may be able to fix this problem by adding the following command-line argument " + + "directly after the 'java' command in your start script: \n'--add-opens java.base/java.lang=ALL-UNNAMED'", cause); + } + + /** + * Accesses using reflection, not supported on Java 9+. + */ + private static class Reflection extends URLClassLoaderAccess { + private static final Method ADD_URL_METHOD; + + static { + Method addUrlMethod; + try { + addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + addUrlMethod.setAccessible(true); + } catch (Exception e) { + addUrlMethod = null; + } + ADD_URL_METHOD = addUrlMethod; + } + + private static boolean isSupported() { + return ADD_URL_METHOD != null; + } + + Reflection(URLClassLoader classLoader) { + super(classLoader); + } + + @Override + public void addURL(@NonNull URL url) { + try { + ADD_URL_METHOD.invoke(super.classLoader, url); + } catch (ReflectiveOperationException e) { + URLClassLoaderAccess.throwError(e); + } + } + } + + /** + * Accesses using sun.misc.Unsafe, supported on Java 9+. + * + * @author Vaishnav Anil (https://github.com/slimjar/slimjar) + */ + private static class Unsafe extends URLClassLoaderAccess { + private static final sun.misc.Unsafe UNSAFE; + + static { + sun.misc.Unsafe unsafe; + try { + Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + unsafe = (sun.misc.Unsafe) unsafeField.get(null); + } catch (Throwable t) { + unsafe = null; + } + UNSAFE = unsafe; + } + + private static boolean isSupported() { + return UNSAFE != null; + } + + private final Collection unopenedURLs; + private final Collection pathURLs; + + @SuppressWarnings("unchecked") + Unsafe(URLClassLoader classLoader) { + super(classLoader); + + Collection unopenedURLs; + Collection pathURLs; + try { + Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp"); + unopenedURLs = (Collection) fetchField(ucp.getClass(), ucp, "unopenedUrls"); + pathURLs = (Collection) fetchField(ucp.getClass(), ucp, "path"); + } catch (Throwable e) { + unopenedURLs = null; + pathURLs = null; + } + + this.unopenedURLs = unopenedURLs; + this.pathURLs = pathURLs; + } + + private static Object fetchField(final Class clazz, final Object object, final String name) throws NoSuchFieldException { + Field field = clazz.getDeclaredField(name); + long offset = UNSAFE.objectFieldOffset(field); + return UNSAFE.getObject(object, offset); + } + + @Override + public void addURL(@NonNull URL url) { + if (this.unopenedURLs == null || this.pathURLs == null) { + URLClassLoaderAccess.throwError(new NullPointerException("unopenedURLs or pathURLs")); + } + + this.unopenedURLs.add(url); + this.pathURLs.add(url); + } + } + + private static class Noop extends URLClassLoaderAccess { + private static final Noop INSTANCE = new Noop(); + + private Noop() { + super(null); + } + + @Override + public void addURL(@NonNull URL url) { + URLClassLoaderAccess.throwError(null); + } + } + +} From ba522597a893c6e8aae86420ebeee7c81106d52e Mon Sep 17 00:00:00 2001 From: Callum Seabrook Date: Fri, 20 Aug 2021 18:16:39 +0100 Subject: [PATCH 021/314] Update to Gradle 7 (#3051) --- api/build.gradle | 85 +++--- build.gradle | 14 +- bukkit-legacy/build.gradle | 12 +- bukkit-legacy/loader/build.gradle | 13 +- .../loader/src/main/resources/plugin.yml | 2 +- bukkit/build.gradle | 11 +- bukkit/loader/build.gradle | 13 +- bukkit/loader/src/main/resources/plugin.yml | 2 +- bungee/build.gradle | 8 +- bungee/loader/build.gradle | 13 +- bungee/loader/src/main/resources/plugin.yml | 2 +- common/build.gradle | 54 ++-- fabric/build.gradle | 20 +- fabric/src/main/resources/fabric.mod.json | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 257 +++++++++++------- nukkit/build.gradle | 6 +- nukkit/loader/build.gradle | 13 +- nukkit/loader/src/main/resources/plugin.yml | 2 +- sponge/build.gradle | 14 +- sponge/sponge-service-api6/build.gradle | 4 +- sponge/sponge-service-api7/build.gradle | 4 +- sponge/sponge-service/build.gradle | 2 +- velocity/build.gradle | 8 +- 25 files changed, 304 insertions(+), 259 deletions(-) diff --git a/api/build.gradle b/api/build.gradle index 02d3dd16d..564464559 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -50,56 +50,63 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass sign configurations.archives } - uploadArchives { + publishing { repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2/') { - authentication(userName: sonatypeUsername, password: sonatypePassword) + maven { + def releasesRepoUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' + def snapshotsRepoUrl = 'https://oss.sonatype.org/content/repositories/snapshots/' + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + + credentials { + username sonatypeUsername + password sonatypePassword } + } + } - snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') { - authentication(userName: sonatypeUsername, password: sonatypePassword) - } + publications { + mavenJava(MavenPublication) { + artifactId = 'api' + + from components.java + artifact sourcesJar + artifact javadocJar pom { - project { - name 'LuckPerms API' - description 'A permissions plugin for Minecraft servers.' - url 'https://luckperms.net' - - licenses { - license { - name 'MIT' - url 'https://opensource.org/licenses/MIT' - } + name 'LuckPerms API' + description 'A permissions plugin for Minecraft servers.' + url 'https://luckperms.net' + + licenses { + license { + name 'MIT' + url 'https://opensource.org/licenses/MIT' } + } - developers { - developer { - id 'lucko' - name 'Luck' - url 'https://lucko.me' - email 'git@lucko.me' - } + developers { + developer { + id 'lucko' + name 'Luck' + url 'https://lucko.me' + email 'git@lucko.me' } + } - scm { - connection 'scm:git:https://github.com/lucko/LuckPerms.git' - developerConnection 'scm:git:git@github.com:lucko/LuckPerms.git' - url 'https://github.com/lucko/LuckPerms' - } + scm { + connection 'scm:git:https://github.com/lucko/LuckPerms.git' + developerConnection 'scm:git:git@github.com:lucko/LuckPerms.git' + url 'https://github.com/lucko/LuckPerms' + } - ciManagement { - system 'Jenkins' - url 'https://ci.lucko.me/job/LuckPerms' - } + ciManagement { + system 'Jenkins' + url 'https://ci.lucko.me/job/LuckPerms' + } - issueManagement { - system 'GitHub' - url 'https://github.com/lucko/LuckPerms/issues' - } + issueManagement { + system 'GitHub' + url 'https://github.com/lucko/LuckPerms/issues' } } } diff --git a/build.gradle b/build.gradle index ec7ec634f..8fc9ce88d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,19 +1,13 @@ -buildscript { - repositories { - maven { url 'https://plugins.gradle.org/m2' } - } - - dependencies { - classpath 'gradle.plugin.net.minecrell:licenser:0.4.1' - } +plugins { + id 'org.cadixdev.licenser' version '0.6.1' apply false } defaultTasks 'licenseFormat', 'build' subprojects { apply plugin: 'java' - apply plugin: 'maven' - apply plugin: 'net.minecrell.licenser' + apply plugin: 'maven-publish' + apply plugin: 'org.cadixdev.licenser' group = 'me.lucko.luckperms' version = '5.3-SNAPSHOT' diff --git a/bukkit-legacy/build.gradle b/bukkit-legacy/build.gradle index bf3c3d79a..ace633bcb 100644 --- a/bukkit-legacy/build.gradle +++ b/bukkit-legacy/build.gradle @@ -1,15 +1,15 @@ plugins { - id 'com.github.johnrengelman.shadow' version '2.0.4' + id 'com.github.johnrengelman.shadow' version '7.0.0' } dependencies { - compile project(':bukkit') - compile 'com.google.code.gson:gson:2.7' - compile 'com.google.guava:guava:19.0' + implementation project(':bukkit') + implementation 'com.google.code.gson:gson:2.7' + implementation 'com.google.guava:guava:19.0' } shadowJar { - archiveName = 'luckperms-bukkitlegacy.jarinjar' + archiveFileName = 'luckperms-bukkitlegacy.jarinjar' dependencies { include(dependency('me.lucko.luckperms:.*')) @@ -41,4 +41,4 @@ shadowJar { artifacts { archives shadowJar -} \ No newline at end of file +} diff --git a/bukkit-legacy/loader/build.gradle b/bukkit-legacy/loader/build.gradle index f0dda1442..a02a45bf5 100644 --- a/bukkit-legacy/loader/build.gradle +++ b/bukkit-legacy/loader/build.gradle @@ -9,19 +9,18 @@ repositories { dependencies { compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT' - compile project(':api') - compile project(':common:loader-utils') + implementation project(':api') + implementation project(':common:loader-utils') } processResources { - from(sourceSets.main.resources.srcDirs) { - expand 'pluginVersion': project.ext.fullVersion - include 'plugin.yml' + filesMatching('plugin.yml') { + expand 'version': project.ext.fullVersion } } shadowJar { - archiveName = "LuckPerms-Bukkit-Legacy-${project.ext.fullVersion}.jar" + archiveFileName = "LuckPerms-Bukkit-Legacy-${project.ext.fullVersion}.jar" from { project(':bukkit-legacy').tasks.shadowJar.archiveFile @@ -30,4 +29,4 @@ shadowJar { artifacts { archives shadowJar -} \ No newline at end of file +} diff --git a/bukkit-legacy/loader/src/main/resources/plugin.yml b/bukkit-legacy/loader/src/main/resources/plugin.yml index d0d010212..c892df5c7 100644 --- a/bukkit-legacy/loader/src/main/resources/plugin.yml +++ b/bukkit-legacy/loader/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LuckPerms -version: ${pluginVersion} +version: @pluginVersion@ description: A permissions plugin author: Luck website: https://luckperms.net diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 458e2a71c..b84107fbd 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -1,5 +1,6 @@ plugins { - id 'com.github.johnrengelman.shadow' version '2.0.4' + id 'com.github.johnrengelman.shadow' version '7.0.0' + id 'java' } repositories { @@ -8,7 +9,7 @@ repositories { } dependencies { - compile project(':common') + implementation project(':common') compileOnly project(':common:loader-utils') compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT' @@ -18,14 +19,14 @@ dependencies { exclude(module: 'adventure-nbt') } compileOnly 'me.lucko:commodore:1.9' - compileOnly('net.milkbowl.vault:VaultAPI:1.6') { + compileOnly('net.milkbowl.vault:VaultAPI:1.7') { exclude(module: 'bukkit') } compileOnly 'lilypad.client.connect:api:0.0.1-SNAPSHOT' } shadowJar { - archiveName = 'luckperms-bukkit.jarinjar' + archiveFileName = 'luckperms-bukkit.jarinjar' dependencies { include(dependency('me.lucko.luckperms:.*')) @@ -52,4 +53,4 @@ shadowJar { artifacts { archives shadowJar -} \ No newline at end of file +} diff --git a/bukkit/loader/build.gradle b/bukkit/loader/build.gradle index f6c56513b..564829b37 100644 --- a/bukkit/loader/build.gradle +++ b/bukkit/loader/build.gradle @@ -9,19 +9,18 @@ repositories { dependencies { compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT' - compile project(':api') - compile project(':common:loader-utils') + implementation project(':api') + implementation project(':common:loader-utils') } processResources { - from(sourceSets.main.resources.srcDirs) { - expand 'pluginVersion': project.ext.fullVersion - include 'plugin.yml' + filesMatching('plugin.yml') { + expand 'version': project.ext.fullVersion } } shadowJar { - archiveName = "LuckPerms-Bukkit-${project.ext.fullVersion}.jar" + archiveFileName = "LuckPerms-Bukkit-${project.ext.fullVersion}.jar" from { project(':bukkit').tasks.shadowJar.archiveFile @@ -30,4 +29,4 @@ shadowJar { artifacts { archives shadowJar -} \ No newline at end of file +} diff --git a/bukkit/loader/src/main/resources/plugin.yml b/bukkit/loader/src/main/resources/plugin.yml index 708025aee..adbbb3fd2 100644 --- a/bukkit/loader/src/main/resources/plugin.yml +++ b/bukkit/loader/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LuckPerms -version: ${pluginVersion} +version: @pluginVersion@ description: A permissions plugin author: Luck website: https://luckperms.net diff --git a/bungee/build.gradle b/bungee/build.gradle index 58bd22eba..bc9dd8dbb 100644 --- a/bungee/build.gradle +++ b/bungee/build.gradle @@ -1,12 +1,12 @@ plugins { - id 'com.github.johnrengelman.shadow' version '2.0.4' + id 'com.github.johnrengelman.shadow' version '7.0.0' } dependencies { - compile project(':common') + implementation project(':common') compileOnly project(':common:loader-utils') - compileOnly 'net.md-5:bungeecord-api:1.15-SNAPSHOT' + compileOnly 'net.md-5:bungeecord-api:1.16-R0.4' compileOnly('me.lucko:adventure-platform-bungeecord:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') @@ -16,7 +16,7 @@ dependencies { } shadowJar { - archiveName = 'luckperms-bungee.jarinjar' + archiveFileName = 'luckperms-bungee.jarinjar' dependencies { include(dependency('me.lucko.luckperms:.*')) diff --git a/bungee/loader/build.gradle b/bungee/loader/build.gradle index 80f237a8e..014465fb5 100644 --- a/bungee/loader/build.gradle +++ b/bungee/loader/build.gradle @@ -5,19 +5,18 @@ plugins { dependencies { compileOnly 'net.md-5:bungeecord-api:1.15-SNAPSHOT' - compile project(':api') - compile project(':common:loader-utils') + implementation project(':api') + implementation project(':common:loader-utils') } processResources { - from(sourceSets.main.resources.srcDirs) { - expand 'pluginVersion': project.ext.fullVersion - include 'plugin.yml' + filesMatching('plugin.yml') { + expand 'version': project.ext.fullVersion } } shadowJar { - archiveName = "LuckPerms-Bungee-${project.ext.fullVersion}.jar" + archiveFileName = "LuckPerms-Bungee-${project.ext.fullVersion}.jar" from { project(':bungee').tasks.shadowJar.archiveFile @@ -26,4 +25,4 @@ shadowJar { artifacts { archives shadowJar -} \ No newline at end of file +} diff --git a/bungee/loader/src/main/resources/plugin.yml b/bungee/loader/src/main/resources/plugin.yml index 7b71717d3..58e7f88a9 100644 --- a/bungee/loader/src/main/resources/plugin.yml +++ b/bungee/loader/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LuckPerms -version: ${pluginVersion} +version: @pluginVersion@ description: A permissions plugin author: Luck main: me.lucko.luckperms.bungee.loader.BungeeLoaderPlugin diff --git a/common/build.gradle b/common/build.gradle index 2e83a49bb..d819bf38a 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'java-library' +} + test { useJUnitPlatform { excludeTags 'dependency_checksum' @@ -5,69 +9,69 @@ test { } dependencies { - testCompile 'org.junit.jupiter:junit-jupiter-api:5.7.0' - testCompile 'org.junit.jupiter:junit-jupiter-engine:5.7.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.7.0' - compile project(':api') - compile 'org.checkerframework:checker-qual:3.12.0' + api project(':api') + api 'org.checkerframework:checker-qual:3.12.0' compileOnly project(':common:loader-utils') compileOnly 'org.slf4j:slf4j-api:1.7.30' compileOnly 'org.apache.logging.log4j:log4j-api:2.14.0' - compile('net.kyori:adventure-api:4.8.1') { + api('net.kyori:adventure-api:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'checker-qual') exclude(module: 'annotations') } - compile('net.kyori:adventure-text-serializer-gson:4.8.1') { + api('net.kyori:adventure-text-serializer-gson:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'gson') } - compile('net.kyori:adventure-text-serializer-legacy:4.8.1') { + api('net.kyori:adventure-text-serializer-legacy:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } - compile('net.kyori:adventure-text-serializer-plain:4.8.1') { + api('net.kyori:adventure-text-serializer-plain:4.8.1') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } - compile('net.kyori:event-api:3.0.0') { + api('net.kyori:event-api:3.0.0') { exclude(module: 'checker-qual') exclude(module: 'guava') } - compile 'com.google.code.gson:gson:2.7' - compile 'com.google.guava:guava:19.0' + api 'com.google.code.gson:gson:2.7' + api 'com.google.guava:guava:19.0' - compile 'com.github.ben-manes.caffeine:caffeine:2.9.0' - compile 'com.squareup.okhttp3:okhttp:3.14.9' - compile 'com.squareup.okio:okio:1.17.5' - compile 'net.bytebuddy:byte-buddy:1.10.22' - compile('org.spongepowered:configurate-core:3.7.2') { + api 'com.github.ben-manes.caffeine:caffeine:2.9.0' + api 'com.squareup.okhttp3:okhttp:3.14.9' + api 'com.squareup.okio:okio:1.17.5' + api 'net.bytebuddy:byte-buddy:1.10.22' + api('org.spongepowered:configurate-core:3.7.2') { transitive = false } - compile('org.spongepowered:configurate-yaml:3.7.2') { + api('org.spongepowered:configurate-yaml:3.7.2') { transitive = false } - compile('org.spongepowered:configurate-gson:3.7.2') { + api('org.spongepowered:configurate-gson:3.7.2') { transitive = false } - compile ('org.spongepowered:configurate-hocon:3.7.2') { + api('org.spongepowered:configurate-hocon:3.7.2') { transitive = false } - compile('me.lucko.configurate:configurate-toml:3.7') { + api('me.lucko.configurate:configurate-toml:3.7') { transitive = false } - compile 'com.zaxxer:HikariCP:4.0.3' - compile 'redis.clients:jedis:3.5.2' - compile 'com.rabbitmq:amqp-client:5.12.0' - compile 'org.mongodb:mongo-java-driver:3.12.8' - compile 'org.yaml:snakeyaml:1.28' + api 'com.zaxxer:HikariCP:4.0.3' + api 'redis.clients:jedis:3.5.2' + api 'com.rabbitmq:amqp-client:5.12.0' + api 'org.mongodb:mongo-java-driver:3.12.8' + api 'org.yaml:snakeyaml:1.28' } diff --git a/fabric/build.gradle b/fabric/build.gradle index da2275c27..85b9e2f52 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,7 +1,7 @@ import net.fabricmc.loom.task.RemapJarTask plugins { - id 'com.github.johnrengelman.shadow' version '4.0.1' + id 'com.github.johnrengelman.shadow' version '7.0.0' id 'fabric-loom' version '0.6-SNAPSHOT' } @@ -36,24 +36,18 @@ dependencies { include(modImplementation('me.lucko:fabric-permissions-api:0.1-SNAPSHOT')) - compile project(':common') + implementation project(':common') } processResources { inputs.property 'version', project.ext.fullVersion - - from(sourceSets.main.resources.srcDirs) { - include 'fabric.mod.json' + filesMatching('fabric-mod.json') { expand 'version': project.ext.fullVersion } - - from(sourceSets.main.resources.srcDirs) { - exclude 'fabric.mod.json' - } } shadowJar { - archiveName = "luckpermsfabric-${project.ext.fullVersion}-dev.jar" + archiveFileName = "luckpermsfabric-${project.ext.fullVersion}-dev.jar" dependencies { exclude('net.fabricmc:.*') @@ -84,9 +78,9 @@ shadowJar { task remappedShadowJar(type: RemapJarTask) { dependsOn tasks.shadowJar - input = tasks.shadowJar.archivePath - addNestedDependencies = true - archiveName = "LuckPerms-Fabric-${project.ext.fullVersion}.jar" + input.set(tasks.shadowJar.archiveFile) + addNestedDependencies.set(true) + archiveFileName = "LuckPerms-Fabric-${project.ext.fullVersion}.jar" } tasks.assemble.dependsOn tasks.remappedShadowJar diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 8a32a743b..1dcabbce3 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "luckperms", - "version": "${version}", + "version": "@version@", "name": "LuckPerms", "icon": "assets/luckperms/icon.png", "description": "A permissions plugin (mod)", diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 18435 zcmY&<19zBR)MXm8v2EM7ZQHi-#I|kQZfv7Tn#Q)%81v4zX3d)U4d4 zYYc!v@NU%|U;_sM`2z(4BAilWijmR>4U^KdN)D8%@2KLcqkTDW%^3U(Wg>{qkAF z&RcYr;D1I5aD(N-PnqoEeBN~JyXiT(+@b`4Pv`;KmkBXYN48@0;iXuq6!ytn`vGp$ z6X4DQHMx^WlOek^bde&~cvEO@K$oJ}i`T`N;M|lX0mhmEH zuRpo!rS~#&rg}ajBdma$$}+vEhz?JAFUW|iZEcL%amAg_pzqul-B7Itq6Y_BGmOCC zX*Bw3rFz3R)DXpCVBkI!SoOHtYstv*e-May|+?b80ZRh$MZ$FerlC`)ZKt} zTd0Arf9N2dimjs>mg5&@sfTPsRXKXI;0L~&t+GH zkB<>wxI9D+k5VHHcB7Rku{Z>i3$&hgd9Mt_hS_GaGg0#2EHzyV=j=u5xSyV~F0*qs zW{k9}lFZ?H%@4hII_!bzao!S(J^^ZZVmG_;^qXkpJb7OyR*sPL>))Jx{K4xtO2xTr@St!@CJ=y3q2wY5F`77Tqwz8!&Q{f7Dp zifvzVV1!Dj*dxG%BsQyRP6${X+Tc$+XOG zzvq5xcC#&-iXlp$)L=9t{oD~bT~v^ZxQG;FRz|HcZj|^L#_(VNG)k{=_6|6Bs-tRNCn-XuaZ^*^hpZ@qwi`m|BxcF6IWc?_bhtK_cDZRTw#*bZ2`1@1HcB`mLUmo_>@2R&nj7&CiH zF&laHkG~7#U>c}rn#H)q^|sk+lc!?6wg0xy`VPn!{4P=u@cs%-V{VisOxVqAR{XX+ zw}R;{Ux@6A_QPka=48|tph^^ZFjSHS1BV3xfrbY84^=?&gX=bmz(7C({=*oy|BEp+ zYgj;<`j)GzINJA>{HeSHC)bvp6ucoE`c+6#2KzY9)TClmtEB1^^Mk)(mXWYvup02e%Ghm9qyjz#fO3bNGBX} zFiB>dvc1+If!>I10;qZk`?6pEd*(?bI&G*3YLt;MWw&!?=Mf7%^Op?qnyXWur- zwX|S^P>jF?{m9c&mmK-epCRg#WB+-VDe!2d2~YVoi%7_q(dyC{(}zB${!ElKB2D}P z7QNFM!*O^?FrPMGZ}wQ0TrQAVqZy!weLhu_Zq&`rlD39r*9&2sJHE(JT0EY5<}~x@ z1>P0!L2IFDqAB!($H9s2fI`&J_c+5QT|b#%99HA3@zUWOuYh(~7q7!Pf_U3u!ij5R zjFzeZta^~RvAmd_TY+RU@e}wQaB_PNZI26zmtzT4iGJg9U(Wrgrl>J%Z3MKHOWV(? zj>~Ph$<~8Q_sI+)$DOP^9FE6WhO09EZJ?1W|KidtEjzBX3RCLUwmj9qH1CM=^}MaK z59kGxRRfH(n|0*lkE?`Rpn6d^u5J6wPfi0WF(rucTv(I;`aW)3;nY=J=igkjsn?ED ztH&ji>}TW8)o!Jg@9Z}=i2-;o4#xUksQHu}XT~yRny|kg-$Pqeq!^78xAz2mYP9+4 z9gwAoti2ICvUWxE&RZ~}E)#M8*zy1iwz zHqN%q;u+f6Ti|SzILm0s-)=4)>eb5o-0K zbMW8ecB4p^6OuIX@u`f{>Yn~m9PINEl#+t*jqalwxIx=TeGB9(b6jA}9VOHnE$9sC zH`;epyH!k-3kNk2XWXW!K`L_G!%xOqk0ljPCMjK&VweAxEaZ==cT#;!7)X&C|X{dY^IY(e4D#!tx^vV3NZqK~--JW~wtXJ8X19adXim?PdN(|@o(OdgH3AiHts~?#QkolO?*=U_buYC&tQ3sc(O5HGHN~=6wB@dgIAVT$ z_OJWJ^&*40Pw&%y^t8-Wn4@l9gOl`uU z{Uda_uk9!Iix?KBu9CYwW9Rs=yt_lE11A+k$+)pkY5pXpocxIEJe|pTxwFgB%Kpr&tH;PzgOQ&m|(#Otm?@H^r`v)9yiR8v&Uy>d#TNdRfyN4Jk;`g zp+jr5@L2A7TS4=G-#O<`A9o;{En5!I8lVUG?!PMsv~{E_yP%QqqTxxG%8%KxZ{uwS zOT+EA5`*moN8wwV`Z=wp<3?~f#frmID^K?t7YL`G^(X43gWbo!6(q*u%HxWh$$^2EOq`Hj zp=-fS#Av+s9r-M)wGIggQ)b<@-BR`R8l1G@2+KODmn<_$Tzb7k35?e8;!V0G>`(!~ zY~qZz!6*&|TupOcnvsQYPbcMiJ!J{RyfezB^;fceBk znpA1XS)~KcC%0^_;ihibczSxwBuy;^ksH7lwfq7*GU;TLt*WmUEVQxt{ zKSfJf;lk$0XO8~48Xn2dnh8tMC9WHu`%DZj&a`2!tNB`5%;Md zBs|#T0Ktf?vkWQ)Y+q!At1qgL`C|nbzvgc(+28Q|4N6Geq)Il%+I5c@t02{9^=QJ?=h2BTe`~BEu=_u3xX2&?^zwcQWL+)7dI>JK0g8_`W1n~ zMaEP97X>Ok#=G*nkPmY`VoP8_{~+Rp7DtdSyWxI~?TZHxJ&=6KffcO2Qx1?j7=LZA z?GQt`oD9QpXw+s7`t+eeLO$cpQpl9(6h3_l9a6OUpbwBasCeCw^UB6we!&h9Ik@1zvJ`j4i=tvG9X8o34+N|y(ay~ho$f=l z514~mP>Z>#6+UxM<6@4z*|hFJ?KnkQBs_9{H(-v!_#Vm6Z4(xV5WgWMd3mB9A(>@XE292#k(HdI7P zJkQ2)`bQXTKlr}{VrhSF5rK9TsjtGs0Rs&nUMcH@$ZX_`Hh$Uje*)(Wd&oLW($hZQ z_tPt`{O@f8hZ<}?aQc6~|9iHt>=!%We3=F9yIfiqhXqp=QUVa!@UY@IF5^dr5H8$R zIh{=%S{$BHG+>~a=vQ={!B9B=<-ID=nyjfA0V8->gN{jRL>Qc4Rc<86;~aY+R!~Vs zV7MI~gVzGIY`B*Tt@rZk#Lg}H8sL39OE31wr_Bm%mn}8n773R&N)8B;l+-eOD@N$l zh&~Wz`m1qavVdxwtZLACS(U{rAa0;}KzPq9r76xL?c{&GaG5hX_NK!?)iq`t7q*F# zFoKI{h{*8lb>&sOeHXoAiqm*vV6?C~5U%tXR8^XQ9Y|(XQvcz*>a?%HQ(Vy<2UhNf zVmGeOO#v159KV@1g`m%gJ)XGPLa`a|?9HSzSSX{j;)xg>G(Ncc7+C>AyAWYa(k}5B3mtzg4tsA=C^Wfezb1&LlyrBE1~kNfeiubLls{C)!<%#m@f}v^o+7<VZ6!FZ;JeiAG@5vw7Li{flC8q1%jD_WP2ApBI{fQ}kN zhvhmdZ0bb5(qK@VS5-)G+@GK(tuF6eJuuV5>)Odgmt?i_`tB69DWpC~e8gqh!>jr_ zL1~L0xw@CbMSTmQflpRyjif*Y*O-IVQ_OFhUw-zhPrXXW>6X}+73IoMsu2?uuK3lT>;W#38#qG5tDl66A7Y{mYh=jK8Se!+f=N7%nv zYSHr6a~Nxd`jqov9VgII{%EpC_jFCEc>>SND0;}*Ja8Kv;G)MK7?T~h((c&FEBcQq zvUU1hW2^TX(dDCeU@~a1LF-(+#lz3997A@pipD53&Dr@III2tlw>=!iGabjXzbyUJ z4Hi~M1KCT-5!NR#I%!2Q*A>mqI{dpmUa_mW)%SDs{Iw1LG}0y=wbj@0ba-`q=0!`5 zr(9q1p{#;Rv2CY!L#uTbs(UHVR5+hB@m*zEf4jNu3(Kj$WwW|v?YL*F_0x)GtQC~! zzrnZRmBmwt+i@uXnk05>uR5&1Ddsx1*WwMrIbPD3yU*2By`71pk@gt{|H0D<#B7&8 z2dVmXp*;B)SWY)U1VSNs4ds!yBAj;P=xtatUx^7_gC5tHsF#vvdV;NmKwmNa1GNWZ zi_Jn-B4GnJ%xcYWD5h$*z^haku#_Irh818x^KB)3-;ufjf)D0TE#6>|zFf@~pU;Rs zNw+}c9S+6aPzxkEA6R%s*xhJ37wmgc)-{Zd1&mD5QT}4BQvczWr-Xim>(P^)52`@R z9+Z}44203T5}`AM_G^Snp<_KKc!OrA(5h7{MT^$ZeDsSr(R@^kI?O;}QF)OU zQ9-`t^ys=6DzgLcWt0U{Q(FBs22=r zKD%fLQ^5ZF24c-Z)J{xv?x$&4VhO^mswyb4QTIofCvzq+27*WlYm;h@;Bq%i;{hZA zM97mHI6pP}XFo|^pRTuWQzQs3B-8kY@ajLV!Fb?OYAO3jFv*W-_;AXd;G!CbpZt04iW`Ie^_+cQZGY_Zd@P<*J9EdRsc>c=edf$K|;voXRJ zk*aC@@=MKwR120(%I_HX`3pJ+8GMeO>%30t?~uXT0O-Tu-S{JA;zHoSyXs?Z;fy58 zi>sFtI7hoxNAdOt#3#AWFDW)4EPr4kDYq^`s%JkuO7^efX+u#-qZ56aoRM!tC^P6O zP(cFuBnQGjhX(^LJ(^rVe4-_Vk*3PkBCj!?SsULdmVr0cGJM^=?8b0^DuOFq>0*yA zk1g|C7n%pMS0A8@Aintd$fvRbH?SNdRaFrfoAJ=NoX)G5Gr}3-$^IGF+eI&t{I-GT zp=1fj)2|*ur1Td)+s&w%p#E6tDXX3YYOC{HGHLiCvv?!%%3DO$B$>A}aC;8D0Ef#b z{7NNqC8j+%1n95zq8|hFY`afAB4E)w_&7?oqG0IPJZv)lr{MT}>9p?}Y`=n+^CZ6E zKkjIXPub5!82(B-O2xQojW^P(#Q*;ETpEr^+Wa=qDJ9_k=Wm@fZB6?b(u?LUzX(}+ zE6OyapdG$HC& z&;oa*ALoyIxVvB2cm_N&h&{3ZTuU|aBrJlGOLtZc3KDx)<{ z27@)~GtQF@%6B@w3emrGe?Cv_{iC@a#YO8~OyGRIvp@%RRKC?fclXMP*6GzBFO z5U4QK?~>AR>?KF@I;|(rx(rKxdT9-k-anYS+#S#e1SzKPslK!Z&r8iomPsWG#>`Ld zJ<#+8GFHE!^wsXt(s=CGfVz5K+FHYP5T0E*?0A-z*lNBf)${Y`>Gwc@?j5{Q|6;Bl zkHG1%r$r&O!N^><8AEL+=y(P$7E6hd=>BZ4ZZ9ukJ2*~HR4KGvUR~MUOe$d>E5UK3 z*~O2LK4AnED}4t1Fs$JgvPa*O+WeCji_cn1@Tv7XQ6l@($F1K%{E$!naeX)`bfCG> z8iD<%_M6aeD?a-(Qqu61&fzQqC(E8ksa%CulMnPvR35d{<`VsmaHyzF+B zF6a@1$CT0xGVjofcct4SyxA40uQ`b#9kI)& z?B67-12X-$v#Im4CVUGZHXvPWwuspJ610ITG*A4xMoRVXJl5xbk;OL(;}=+$9?H`b z>u2~yd~gFZ*V}-Q0K6E@p}mtsri&%Zep?ZrPJmv`Qo1>94Lo||Yl)nqwHXEbe)!g( zo`w|LU@H14VvmBjjkl~=(?b{w^G$~q_G(HL`>|aQR%}A64mv0xGHa`S8!*Wb*eB}` zZh)&rkjLK!Rqar)UH)fM<&h&@v*YyOr!Xk2OOMV%$S2mCRdJxKO1RL7xP_Assw)bb z9$sQ30bapFfYTS`i1PihJZYA#0AWNmp>x(;C!?}kZG7Aq?zp!B+gGyJ^FrXQ0E<>2 zCjqZ(wDs-$#pVYP3NGA=en<@_uz!FjFvn1&w1_Igvqs_sL>ExMbcGx4X5f%`Wrri@ z{&vDs)V!rd=pS?G(ricfwPSg(w<8P_6=Qj`qBC7_XNE}1_5>+GBjpURPmvTNE7)~r)Y>ZZecMS7Ro2` z0}nC_GYo3O7j|Wux?6-LFZs%1IV0H`f`l9or-8y0=5VGzjPqO2cd$RRHJIY06Cnh- ztg@Pn1OeY=W`1Mv3`Ti6!@QIT{qcC*&vptnX4Pt1O|dWv8u2s|(CkV`)vBjAC_U5` zCw1f&c4o;LbBSp0=*q z3Y^horBAnR)u=3t?!}e}14%K>^562K!)Vy6r~v({5{t#iRh8WIL|U9H6H97qX09xp zjb0IJ^9Lqxop<-P*VA0By@In*5dq8Pr3bTPu|ArID*4tWM7w+mjit0PgmwLV4&2PW z3MnIzbdR`3tPqtUICEuAH^MR$K_u8~-U2=N1)R=l>zhygus44>6V^6nJFbW-`^)f} zI&h$FK)Mo*x?2`0npTD~jRd}5G~-h8=wL#Y-G+a^C?d>OzsVl7BFAaM==(H zR;ARWa^C3J)`p~_&FRsxt|@e+M&!84`eq)@aO9yBj8iifJv0xVW4F&N-(#E=k`AwJ z3EFXWcpsRlB%l_0Vdu`0G(11F7( zsl~*@XP{jS@?M#ec~%Pr~h z2`M*lIQaolzWN&;hkR2*<=!ORL(>YUMxOzj(60rQfr#wTrkLO!t{h~qg% zv$R}0IqVIg1v|YRu9w7RN&Uh7z$ijV=3U_M(sa`ZF=SIg$uY|=NdC-@%HtkUSEqJv zg|c}mKTCM=Z8YmsFQu7k{VrXtL^!Cts-eb@*v0B3M#3A7JE*)MeW1cfFqz~^S6OXFOIP&iL;Vpy z4dWKsw_1Wn%Y;eW1YOfeP_r1s4*p1C(iDG_hrr~-I%kA>ErxnMWRYu{IcG{sAW;*t z9T|i4bI*g)FXPpKM@~!@a7LDVVGqF}C@mePD$ai|I>73B+9!Ks7W$pw;$W1B%-rb; zJ*-q&ljb=&41dJ^*A0)7>Wa@khGZ;q1fL(2qW=|38j43mTl_;`PEEw07VKY%71l6p z@F|jp88XEnm1p~<5c*cVXvKlj0{THF=n3sU7g>Ki&(ErR;!KSmfH=?49R5(|c_*xw z4$jhCJ1gWT6-g5EV)Ahg?Nw=}`iCyQ6@0DqUb%AZEM^C#?B-@Hmw?LhJ^^VU>&phJ zlB!n5&>I>@sndh~v$2I2Ue23F?0!0}+9H~jg7E`?CS_ERu75^jSwm%!FTAegT`6s7 z^$|%sj2?8wtPQR>@D3sA0-M-g-vL@47YCnxdvd|1mPymvk!j5W1jHnVB&F-0R5e-vs`@u8a5GKdv`LF7uCfKncI4+??Z4iG@AxuX7 z6+@nP^TZ5HX#*z(!y+-KJ3+Ku0M90BTY{SC^{ z&y2#RZPjfX_PE<<>XwGp;g4&wcXsQ0T&XTi(^f+}4qSFH1%^GYi+!rJo~t#ChTeAX zmR0w(iODzQOL+b&{1OqTh*psAb;wT*drr^LKdN?c?HJ*gJl+%kEH&48&S{s28P=%p z7*?(xFW_RYxJxxILS!kdLIJYu@p#mnQ(?moGD1)AxQd66X6b*KN?o&e`u9#N4wu8% z^Gw#G!@|>c740RXziOR=tdbkqf(v~wS_N^CS^1hN-N4{Dww1lvSWcBTX*&9}Cz|s@ z*{O@jZ4RVHq19(HC9xSBZI0M)E;daza+Q*zayrX~N5H4xJ33BD4gn5Ka^Hj{995z4 zzm#Eo?ntC$q1a?)dD$qaC_M{NW!5R!vVZ(XQqS67xR3KP?rA1^+s3M$60WRTVHeTH z6BJO$_jVx0EGPXy}XK_&x597 zt(o6ArN8vZX0?~(lFGHRtHP{gO0y^$iU6Xt2e&v&ugLxfsl;GD)nf~3R^ACqSFLQ< zV7`cXgry((wDMJB55a6D4J;13$z6pupC{-F+wpToW%k1qKjUS^$Mo zN3@}T!ZdpiV7rkNvqP3KbpEn|9aB;@V;gMS1iSb@ zwyD7!5mfj)q+4jE1dq3H`sEKgrVqk|y8{_vmn8bMOi873!rmnu5S=1=-DFx+Oj)Hi zx?~ToiJqOrvSou?RVALltvMADodC7BOg7pOyc4m&6yd(qIuV5?dYUpYzpTe!BuWKi zpTg(JHBYzO&X1e{5o|ZVU-X5e?<}mh=|eMY{ldm>V3NsOGwyxO2h)l#)rH@BI*TN; z`yW26bMSp=k6C4Ja{xB}s`dNp zE+41IwEwo>7*PA|7v-F#jLN>h#a`Er9_86!fwPl{6yWR|fh?c%qc44uP~Ocm2V*(* zICMpS*&aJjxutxKC0Tm8+FBz;3;R^=ajXQUB*nTN*Lb;mruQHUE<&=I7pZ@F-O*VMkJbI#FOrBM8`QEL5Uy=q5e2 z_BwVH%c0^uIWO0*_qD;0jlPoA@sI7BPwOr-mrp7y`|EF)j;$GYdOtEPFRAKyUuUZS z(N4)*6R*ux8s@pMdC*TP?Hx`Zh{{Ser;clg&}CXriXZCr2A!wIoh;j=_eq3_%n7V} za?{KhXg2cXPpKHc90t6=`>s@QF-DNcTJRvLTS)E2FTb+og(wTV7?$kI?QZYgVBn)& zdpJf@tZ{j>B;<MVHiPl_U&KlqBT)$ic+M0uUQWK|N1 zCMl~@o|}!!7yyT%7p#G4?T^Azxt=D(KP{tyx^lD_(q&|zNFgO%!i%7T`>mUuU^FeR zHP&uClWgXm6iXgI8*DEA!O&X#X(zdrNctF{T#pyax16EZ5Lt5Z=RtAja!x+0Z31U8 zjfaky?W)wzd+66$L>o`n;DISQNs09g{GAv%8q2k>2n8q)O^M}=5r#^WR^=se#WSCt zQ`7E1w4qdChz4r@v6hgR?nsaE7pg2B6~+i5 zcTTbBQ2ghUbC-PV(@xvIR(a>Kh?{%YAsMV#4gt1nxBF?$FZ2~nFLKMS!aK=(`WllA zHS<_7ugqKw!#0aUtQwd#A$8|kPN3Af?Tkn)dHF?_?r#X68Wj;|$aw)Wj2Dkw{6)*^ zZfy!TWwh=%g~ECDCy1s8tTgWCi}F1BvTJ9p3H6IFq&zn#3FjZoecA_L_bxGWgeQup zAAs~1IPCnI@H>g|6Lp^Bk)mjrA3_qD4(D(65}l=2RzF-8@h>|Aq!2K-qxt(Q9w7c^ z;gtx`I+=gKOl;h=#fzSgw-V*YT~2_nnSz|!9hIxFb{~dKB!{H zSi??dnmr@%(1w^Be=*Jz5bZeofEKKN&@@uHUMFr-DHS!pb1I&;x9*${bmg6=2I4Zt zHb5LSvojY7ubCNGhp)=95jQ00sMAC{IZdAFsN!lAVQDeiec^HAu=8);2AKqNTT!&E zo+FAR`!A1#T6w@0A+o%&*yzkvxsrqbrfVTG+@z8l4+mRi@j<&)U9n6L>uZoezW>qS zA4YfO;_9dQSyEYpkWnsk0IY}Nr2m(ql@KuQjLgY-@g z4=$uai6^)A5+~^TvLdvhgfd+y?@+tRE^AJabamheJFnpA#O*5_B%s=t8<;?I;qJ}j z&g-9?hbwWEez-!GIhqpB>nFvyi{>Yv>dPU=)qXnr;3v-cd`l}BV?6!v{|cHDOx@IG z;TSiQQ(8=vlH^rCEaZ@Yw}?4#a_Qvx=}BJuxACxm(E7tP4hki^jU@8A zUS|4tTLd)gr@T|F$1eQXPY%fXb7u}(>&9gsd3It^B{W#6F2_g40cgo1^)@-xO&R5X z>qKon+Nvp!4v?-rGQu#M_J2v+3e+?N-WbgPQWf`ZL{Xd9KO^s{uIHTJ6~@d=mc7i z+##ya1p+ZHELmi%3C>g5V#yZt*jMv( zc{m*Y;7v*sjVZ-3mBuaT{$g+^sbs8Rp7BU%Ypi+c%JxtC4O}|9pkF-p-}F{Z7-+45 zDaJQx&CNR)8x~0Yf&M|-1rw%KW3ScjWmKH%J1fBxUp(;F%E+w!U470e_3%+U_q7~P zJm9VSWmZ->K`NfswW(|~fGdMQ!K2z%k-XS?Bh`zrjZDyBMu74Fb4q^A=j6+Vg@{Wc zPRd5Vy*-RS4p1OE-&8f^Fo}^yDj$rb+^>``iDy%t)^pHSV=En5B5~*|32#VkH6S%9 zxgIbsG+|{-$v7mhOww#v-ejaS>u(9KV9_*X!AY#N*LXIxor9hDv%aie@+??X6@Et=xz>6ev9U>6Pn$g4^!}w2Z%Kpqpp+M%mk~?GE-jL&0xLC zy(`*|&gm#mLeoRU8IU?Ujsv=;ab*URmsCl+r?%xcS1BVF*rP}XRR%MO_C!a9J^fOe>U;Y&3aj3 zX`3?i12*^W_|D@VEYR;h&b^s#Kd;JMNbZ#*x8*ZXm(jgw3!jyeHo14Zq!@_Q`V;Dv zKik~!-&%xx`F|l^z2A92aCt4x*I|_oMH9oeqsQgQDgI0j2p!W@BOtCTK8Jp#txi}7 z9kz);EX-2~XmxF5kyAa@n_$YYP^Hd4UPQ>O0-U^-pw1*n{*kdX`Jhz6{!W=V8a$0S z9mYboj#o)!d$gs6vf8I$OVOdZu7L5%)Vo0NhN`SwrQFhP3y4iXe2uV@(G{N{yjNG( zKvcN{k@pXkxyB~9ucR(uPSZ7{~sC=lQtz&V(^A^HppuN!@B4 zS>B=kb14>M-sR>{`teApuHlca6YXs6&sRvRV;9G!XI08CHS~M$=%T~g5Xt~$exVk` zWP^*0h{W%`>K{BktGr@+?ZP}2t0&smjKEVw@3=!rSjw5$gzlx`{dEajg$A58m|Okx zG8@BTPODSk@iqLbS*6>FdVqk}KKHuAHb0UJNnPm!(XO{zg--&@#!niF4T!dGVdNif z3_&r^3+rfQuV^8}2U?bkI5Ng*;&G>(O4&M<86GNxZK{IgKNbRfpg>+32I>(h`T&uv zUN{PRP&onFj$tn1+Yh|0AF330en{b~R+#i9^QIbl9fBv>pN|k&IL2W~j7xbkPyTL^ z*TFONZUS2f33w3)fdzr?)Yg;(s|||=aWZV(nkDaACGSxNCF>XLJSZ=W@?$*` z#sUftY&KqTV+l@2AP5$P-k^N`Bme-xcWPS|5O~arUq~%(z8z87JFB|llS&h>a>Som zC34(_uDViE!H2jI3<@d+F)LYhY)hoW6)i=9u~lM*WH?hI(yA$X#ip}yYld3RAv#1+sBt<)V_9c4(SN9Fn#$}_F}A-}P>N+8io}I3mh!}> z*~*N}ZF4Zergb;`R_g49>ZtTCaEsCHiFb(V{9c@X0`YV2O^@c6~LXg2AE zhA=a~!ALnP6aO9XOC^X15(1T)3!1lNXBEVj5s*G|Wm4YBPV`EOhU&)tTI9-KoLI-U zFI@adu6{w$dvT(zu*#aW*4F=i=!7`P!?hZy(9iL;Z^De3?AW`-gYTPALhrZ*K2|3_ zfz;6xQN9?|;#_U=4t^uS2VkQ8$|?Ub5CgKOj#Ni5j|(zX>x#K(h7LgDP-QHwok~-I zOu9rn%y97qrtKdG=ep)4MKF=TY9^n6CugQ3#G2yx;{))hvlxZGE~rzZ$qEHy-8?pU#G;bwufgSN6?*BeA!7N3RZEh{xS>>-G1!C(e1^ zzd#;39~PE_wFX3Tv;zo>5cc=md{Q}(Rb?37{;YPtAUGZo7j*yHfGH|TOVR#4ACaM2 z;1R0hO(Gl}+0gm9Bo}e@lW)J2OU4nukOTVKshHy7u)tLH^9@QI-jAnDBp(|J8&{fKu=_97$v&F67Z zq+QsJ=gUx3_h_%=+q47msQ*Ub=gMzoSa@S2>`Y9Cj*@Op4plTc!jDhu51nSGI z^sfZ(4=yzlR}kP2rcHRzAY9@T7f`z>fdCU0zibx^gVg&fMkcl)-0bRyWe12bT0}<@ z^h(RgGqS|1y#M;mER;8!CVmX!j=rfNa6>#_^j{^C+SxGhbSJ_a0O|ae!ZxiQCN2qA zKs_Z#Zy|9BOw6x{0*APNm$6tYVG2F$K~JNZ!6>}gJ_NLRYhcIsxY1z~)mt#Yl0pvC zO8#Nod;iow5{B*rUn(0WnN_~~M4|guwfkT(xv;z)olmj=f=aH#Y|#f_*d1H!o( z!EXNxKxth9w1oRr0+1laQceWfgi8z`YS#uzg#s9-QlTT7y2O^^M1PZx z3YS7iegfp6Cs0-ixlG93(JW4wuE7)mfihw}G~Uue{Xb+#F!BkDWs#*cHX^%(We}3% zT%^;m&Juw{hLp^6eyM}J({luCL_$7iRFA6^8B!v|B9P{$42F>|M`4Z_yA{kK()WcM zu#xAZWG%QtiANfX?@+QQOtbU;Avr*_>Yu0C2>=u}zhH9VLp6M>fS&yp*-7}yo8ZWB z{h>ce@HgV?^HgwRThCYnHt{Py0MS=Ja{nIj5%z;0S@?nGQ`z`*EVs&WWNwbzlk`(t zxDSc)$dD+4G6N(p?K>iEKXIk>GlGKTH{08WvrehnHhh%tgpp&8db4*FLN zETA@<$V=I7S^_KxvYv$Em4S{gO>(J#(Wf;Y%(NeECoG3n+o;d~Bjme-4dldKukd`S zRVAnKxOGjWc;L#OL{*BDEA8T=zL8^`J=2N)d&E#?OMUqk&9j_`GX*A9?V-G zdA5QQ#(_Eb^+wDkDiZ6RXL`fck|rVy%)BVv;dvY#`msZ}{x5fmd! zInmWSxvRgXbJ{unxAi*7=Lt&7_e0B#8M5a=Ad0yX#0rvMacnKnXgh>4iiRq<&wit93n!&p zeq~-o37qf)L{KJo3!{l9l9AQb;&>)^-QO4RhG>j`rBlJ09~cbfNMR_~pJD1$UzcGp zOEGTzz01j$=-kLC+O$r8B|VzBotz}sj(rUGOa7PDYwX~9Tum^sW^xjjoncxSz;kqz z$Pz$Ze|sBCTjk7oM&`b5g2mFtuTx>xl{dj*U$L%y-xeQL~|i>KzdUHeep-Yd@}p&L*ig< zgg__3l9T=nbM3bw0Sq&Z2*FA)P~sx0h634BXz0AxV69cED7QGTbK3?P?MENkiy-mV zZ1xV5ry3zIpy>xmThBL0Q!g+Wz@#?6fYvzmEczs(rcujrfCN=^!iWQ6$EM zaCnRThqt~gI-&6v@KZ78unqgv9j6-%TOxpbV`tK{KaoBbhc}$h+rK)5h|bT6wY*t6st-4$e99+Egb#3ip+ERbve08G@Ref&hP)qB&?>B94?eq5i3k;dOuU#!y-@+&5>~!FZik=z4&4|YHy=~!F254 zQAOTZr26}Nc7jzgJ;V~+9ry#?7Z0o*;|Q)k+@a^87lC}}1C)S))f5tk+lMNqw>vh( z`A9E~5m#b9!ZDBltf7QIuMh+VheCoD7nCFhuzThlhA?|8NCt3w?oWW|NDin&&eDU6 zwH`aY=))lpWG?{fda=-auXYp1WIPu&3 zwK|t(Qiqvc@<;1_W#ALDJ}bR;3&v4$9rP)eAg`-~iCte`O^MY+SaP!w%~+{{1tMo` zbp?T%ENs|mHP)Lsxno=nWL&qizR+!Ib=9i%4=B@(Umf$|7!WVxkD%hfRjvxV`Co<; zG*g4QG_>;RE{3V_DOblu$GYm&!+}%>G*yO{-|V9GYG|bH2JIU2iO}ZvY>}Fl%1!OE zZFsirH^$G>BDIy`8;R?lZl|uu@qWj2T5}((RG``6*05AWsVVa2Iu>!F5U>~7_Tlv{ zt=Dpgm~0QVa5mxta+fUt)I0gToeEm9eJX{yYZ~3sLR&nCuyuFWuiDIVJ+-lwViO(E zH+@Rg$&GLueMR$*K8kOl>+aF84Hss5p+dZ8hbW$=bWNIk0paB!qEK$xIm5{*^ad&( zgtA&gb&6FwaaR2G&+L+Pp>t^LrG*-B&Hv;-s(h0QTuYWdnUObu8LRSZoAVd7SJ;%$ zh%V?58mD~3G2X<$H7I)@x?lmbeeSY7X~QiE`dfQ5&K^FB#9e!6!@d9vrSt!);@ZQZ zO#84N5yH$kjm9X4iY#f+U`FKhg=x*FiDoUeu1O5LcC2w&$~5hKB9ZnH+8BpbTGh5T zi_nfmyQY$vQh%ildbR7T;7TKPxSs#vhKR|uup`qi1PufMa(tNCjRbllakshQgn1)a8OO-j8W&aBc_#q1hKDF5-X$h`!CeT z+c#Ial~fDsGAenv7~f@!icm(~)a3OKi((=^zcOb^qH$#DVciGXslUwTd$gt{7)&#a`&Lp ze%AnL0#U?lAl8vUkv$n>bxH*`qOujO0HZkPWZnE0;}0DSEu1O!hg-d9#{&#B1Dm)L zvN%r^hdEt1vR<4zwshg*0_BNrDWjo65be1&_82SW8#iKWs7>TCjUT;-K~*NxpG2P% zovXUo@S|fMGudVSRQrP}J3-Wxq;4xIxJJC|Y#TQBr>pwfy*%=`EUNE*dr-Y?9y9xK zmh1zS@z{^|UL}v**LNYY!?1qIRPTvr!gNXzE{%=-`oKclPrfMKwn` zUwPeIvLcxkIV>(SZ-SeBo-yw~{p!<&_}eELG?wxp zee-V59%@BtB+Z&Xs=O(@P$}v_qy1m=+`!~r^aT> zY+l?+6(L-=P%m4ScfAYR8;f9dyVw)@(;v{|nO#lAPI1xDHXMYt~-BGiP&9y2OQsYdh7-Q1(vL<$u6W0nxVn-qh=nwuRk}{d!uACozccRGx6~xZQ;=#JCE?OuA@;4 zadp$sm}jfgW4?La(pb!3f0B=HUI{5A4b$2rsB|ZGb?3@CTA{|zBf07pYpQ$NM({C6Srv6%_{rVkCndT=1nS}qyEf}Wjtg$e{ng7Wgz$7itYy0sWW_$qld);iUm85GBH)fk3b=2|5mvflm?~inoVo zDH_%e;y`DzoNj|NgZ`U%a9(N*=~8!qqy0Etkxo#`r!!{|(NyT0;5= z8nVZ6AiM+SjMG8J@6c4_f-KXd_}{My?Se1GWP|@wROFpD^5_lu?I%CBzpwi(`x~xh B8dv}T delta 17845 zcmV)CK*GO}(F4QI1F(Jx4W$DjNjn4p0N4ir06~)x5+0MO2`GQvQyWzj|J`gh3(E#l zNGO!HfVMRRN~%`0q^)g%XlN*vP!O#;m*h5VyX@j-1N|HN;8S1vqEAj=eCdn`)tUB9 zXZjcT^`bL6qvL}gvXj%9vrOD+x!Gc_0{$Zg+6lTXG$bmoEBV z*%y^c-mV0~Rjzv%e6eVI)yl>h;TMG)Ft8lqpR`>&IL&`>KDi5l$AavcVh9g;CF0tY zw_S0eIzKD?Nj~e4raA8wxiiImTRzv6;b6|LFmw)!E4=CiJ4I%&axSey4zE-MIh@*! z*P;K2Mx{xVYPLeagKA}Hj=N=1VrWU`ukuBnc14iBG?B}Uj>?=2UMk4|42=()8KOnc zrJzAxxaEIfjw(CKV6F$35u=1qyf(%cY8fXaS9iS?yetY{mQ#Xyat*7sSoM9fJlZqq zyasQ3>D>6p^`ck^Y|kYYZB*G})uAbQ#7)Jeb~glGz@2rPu}zBWDzo5K$tP<|meKV% z{Swf^eq6NBioF)v&~9NLIxHMTKe6gJ@QQ^A6fA!n#u1C&n`aG7TDXKM1Jly-DwTB` z+6?=Y)}hj;C#r5>&x;MCM4U13nuXVK*}@yRY~W3X%>U>*CB2C^K6_OZsXD!nG2RSX zQg*0)$G3%Es$otA@p_1N!hIPT(iSE=8OPZG+t)oFyD~{nevj0gZen$p>U<7}uRE`t5Mk1f4M0K*5 zbn@3IG5I2mk;8K>*RZ zPV6iL006)S001s%0eYj)9hu1 z9o)iQT9(v*sAuZ|ot){RrZ0Qw4{E0A+!Yx_M~#Pj&OPUM&i$RU=Uxu}e*6Sr2ror= z&?lmvFCO$)BY+^+21E>ENWe`I0{02H<-lz&?})gIVFyMWxX0B|0b?S6?qghp3lDgz z2?0|ALJU=7s-~Lb3>9AA5`#UYCl!Xeh^i@bxs5f&SdiD!WN}CIgq&WI4VCW;M!UJL zX2};d^sVj5oVl)OrkapV-C&SrG)*x=X*ru!2s04TjZ`pY$jP)4+%)7&MlpiZ`lgoF zo_p>^4qGz^(Y*uB10dY2kcIbt=$FIdYNqk;~47wf@)6|nJp z1cocL3zDR9N2Pxkw)dpi&_rvMW&Dh0@T*_}(1JFSc0S~Ph2Sr=vy)u*=TY$i_IHSo zR+&dtWFNxHE*!miRJ%o5@~GK^G~4$LzEYR-(B-b(L*3jyTq}M3d0g6sdx!X3-m&O% zK5g`P179KHJKXpIAAX`A2MFUA;`nXx^b?mboVbQgigIHTU8FI>`q53AjWaD&aowtj z{XyIX>c)*nLO~-WZG~>I)4S1d2q@&?nwL)CVSWqWi&m1&#K1!gt`g%O4s$u^->Dwq ziKc&0O9KQ7000OG0000%03-m(e&Y`S09YWC4iYDSty&3q8^?8ij|8zxaCt!zCFq1@ z9TX4Hl68`nY>}cQNW4Ullqp$~SHO~l1!CdFLKK}ij_t^a?I?C^CvlvnZkwiVn>dl2 z2$V(JN{`5`-8ShF_ek6HNRPBlPuIPYu>TAeAV5O2)35r3*_k(Q-h1+h5pb(Zu%oJ__pBsW0n5ILw`!&QR&YV`g0Fe z(qDM!FX_7;`U3rxX#QHT{f%h;)Eursw=*#qvV)~y%^Uo^% zi-%sMe^uz;#Pe;@{JUu05zT*i=u7mU9{MkT`ft(vPdQZoK&2mg=tnf8FsaNQ+QcPg zB>vP8Rd6Z0JoH5_Q`zldg;hx4azQCq*rRZThqlqTRMzn1O3_rQTrHk8LQ<{5UYN~` zM6*~lOGHyAnx&#yCK{i@%N1Us@=6cw=UQxpSE;<(LnnES%6^q^QhBYQ-VCSmIu8wh z@_LmwcFDfAhIn>`%h7L{)iGBzu`Md4dj-m3C8mA9+BL*<>q z#$7^ttIBOE-=^|zmG`K8yUKT{yjLu2SGYsreN0*~9yhFxn4U};Nv1XXj1fH*v-g=3 z@tCPc`YdzQGLp%zXwo*o$m9j-+~nSWls#s|?PyrHO%SUGdk**X9_=|b)Y%^j_V$3S z>mL2A-V)Q}qb(uZipEFVm?}HWc+%G6_K+S+87g-&RkRQ8-{0APDil115eG|&>WQhU zufO*|e`hFks^cJJmx_qNx{ltSp3aT|XgD5-VxGGXb7gkiOG$w^qMVBDjR8%!Sbh72niHRDV* ziFy8LE+*$j?t^6aZP9qt-ow;hzkmhvy*Hn-X^6?yVMbtNbyqZQ^rXg58`gk+I%Wv} zn_)dRq+3xjc8D%}EQ%nnTF7L7m}o9&*^jf`_qvUhVKY7w9Zgxr-0YHWFRd3$l_6UX zpXt^U&TiC*qZWx#pOG6k?3Tg)pra*fw(O6_45>lUBN1U5Qmc>^DHt)5b~Ntjsw!NI z1n4{$HWFeIi)*qvgK^ui;(81VQc1(wJ8C#tjR>Dkjf{xYC^_B^#qrdCc)uZxtgua6 zk98UGQF|;;k`c+0_z)tQ&9DwLB~&12@D1!*mTz_!3Mp=cg;B7Oq4cKN>5v&dW7q@H zal=g6Ipe`siZN4NZiBrkJCU*x216gmbV(FymgHuG@%%|8sgD?gR&0*{y4n=pukZnd z4=Nl~_>jVfbIehu)pG)WvuUpLR}~OKlW|)=S738Wh^a&L+Vx~KJU25o6%G7+Cy5mB zgmYsgkBC|@K4Jm_PwPoz`_|5QSk}^p`XV`649#jr4Lh^Q>Ne~#6Cqxn$7dNMF=%Va z%z9Ef6QmfoXAlQ3)PF8#3Y% zadcE<1`fd1&Q9fMZZnyI;&L;YPuy#TQ8b>AnXr*SGY&xUb>2678A+Y z8K%HOdgq_4LRFu_M>Ou|kj4W%sPPaV)#zDzN~25klE!!PFz_>5wCxglj7WZI13U5| zEq_YLKPH;v8sEhyG`dV_jozR);a6dBvkauhC;1dk%mr+J*Z6MMH9jqxFk@)&h{mHl zrf^i_d-#mTF=6-T8Rk?(1+rPGgl$9=j%#dkf@x6>czSc`jk7$f!9SrV{do%m!t8{? z_iAi$Qe&GDR#Nz^#uJ>-_?(E$ns)(3)X3cYY)?gFvU+N>nnCoBSmwB2<4L|xH19+4 z`$u#*Gt%mRw=*&|em}h_Y`Pzno?k^8e*hEwfM`A_yz-#vJtUfkGb=s>-!6cHfR$Mz z`*A8jVcz7T{n8M>ZTb_sl{EZ9Ctau4naX7TX?&g^VLE?wZ+}m)=YW4ODRy*lV4%-0 zG1XrPs($mVVfpnqoSihnIFkLdxG9um&n-U|`47l{bnr(|8dmglO7H~yeK7-wDwZXq zaHT($Qy2=MMuj@lir(iyxI1HnMlaJwpX86je}e=2n|Esb6hB?SmtDH3 z2qH6o`33b{;M{mDa5@@~1or8+Zcio*97pi1Jkx6v5MXCaYsb~Ynq)eWpKnF{n)FXZ z?Xd;o7ESu&rtMFr5(yJ(B7V>&0gnDdL*4MZH&eO+r*t!TR98ssbMRaw`7;`SLI8mT z=)hSAt~F=mz;JbDI6g~J%w!;QI(X14AnOu;uve^4wyaP3>(?jSLp+LQ7uU(iib%IyB(d&g@+hg;78M>h7yAeq$ALRoHGkKXA+E z$Sk-hd$Fs2nL4w9p@O*Y$c;U)W#d~)&8Js;i^Dp^* z0*7*zEGj~VehF4sRqSGny*K_CxeF=T^8;^lb}HF125G{kMRV?+hYktZWfNA^Mp7y8 zK~Q?ycf%rr+wgLaHQ|_<6z^eTG7izr@99SG9Q{$PCjJabSz`6L_QJJe7{LzTc$P&pwTy<&3RRUlSHmK;?}=QAhQaDW3#VWcNAH3 zeBPRTDf3?3mfdI$&WOg(nr9Gyzg`&u^o!f2rKJ57D_>p z6|?Vg?h(@(*X=o071{g^le>*>qSbVam`o}sAK8>b|11%e&;%`~b2OP7--q%0^2YDS z`2M`{2QYr1VC)sIW9WOu8<~7Q>^$*Og{KF+kI;wFegvaIDkB%3*%PWtWKSq7l`1YcDxQQ2@nv{J!xWV?G+w6C zhUUxUYVf%(Q(40_xrZB@rbxL=Dj3RV^{*yHd>4n-TOoHVRnazDOxxkS9kiZyN}IN3 zB^5N=* zRSTO+rA<{*P8-$GZdyUNOB=MzddG$*@q>mM;pUIiQ_z)hbE#Ze-IS)9G}Rt$5PSB{ zZZ;#h9nS7Rf1ecW&n(Gpu9}{vXQZ-f`UHIvD?cTbF`YvH*{rgE(zE22pLAQfhg-`U zuh612EpByB(~{w7svCylrBk%5$LCIyuhrGi=yOfca`=8ltKxHcSNfDRt@62QH^R_0 z&eQL6rRk>Dvf6rjMQv5ZXzg}S`HqV69hJT^pPHtdhqsrPJWs|IT9>BvpQa@*(FX6v zG}TYjreQCnH(slMt5{NgUf)qsS1F&Bb(M>$X}tWI&yt2I&-rJbqveuj?5J$`Dyfa2 z)m6Mq0XH@K)Y2v8X=-_4=4niodT&Y7W?$KLQhjA<+R}WTdYjX9>kD+SRS^oOY1{A= zZTId-(@wF^UEWso($wZtrs%e7t<}YaC_;#@`r0LUzKY&|qPJz*y~RHG`E6bypP5AX zN!p0^AUu8uDR>xM-ALFzBxXM~Q3z=}fHWCIG>0&I6x2Iu7&U)49j7qeMI&?qb$=4I zdMmhAJrO%@0f%YW! z^gLByEGSk+R0v4*d4w*N$Ju6z#j%HBI}6y$2en=-@S3=6+yZX94m&1j@s- z7T6|#0$c~dYq9IkA!P)AGkp~S$zYJ1SXZ#RM0|E~Q0PSm?DsT4N3f^)b#h(u9%_V5 zX*&EIX|gD~P!vtx?ra71pl%v)F!W~X2hcE!h8cu@6uKURdmo1-7icN4)ej4H1N~-C zjXgOK+mi#aJv4;`DZ%QUbVVZclkx;9`2kgbAhL^d{@etnm+5N8pB#fyH)bxtZGCAv z(%t0kPgBS{Q2HtjrfI0B$$M0c?{r~2T=zeXo7V&&aprCzww=i*}Atu7g^(*ivauMz~kkB%Vt{Wydlz%%2c26%>0PAbZO zVHx%tK(uzDl#ZZK`cW8TD2)eD77wB@gum{B2bO_jnqGl~01EF_^jx4Uqu1yfA~*&g zXJ`-N?D-n~5_QNF_5+Un-4&l$1b zVlHFqtluoN85b^C{A==lp#hS9J(npJ#6P4aY41r) zzCmv~c77X5L}H%sj>5t&@0heUDy;S1gSOS>JtH1v-k5l}z2h~i3^4NF6&iMb;ZYVE zMw*0%-9GdbpF1?HHim|4+)Zed=Fk<2Uz~GKc^P(Ig@x0&XuX0<-K(gA*KkN&lY2Xu zG054Q8wbK~$jE32#Ba*Id2vkqmfV{U$Nx9vJ;jeI`X+j1kh7hB8$CBTe@ANmT^tI8 z%U>zrTKuECin-M|B*gy(SPd`(_xvxjUL?s137KOyH>U{z01cBcFFt=Fp%d+BK4U;9 zQG_W5i)JASNpK)Q0wQpL<+Ml#cei41kCHe&P9?>p+KJN>I~`I^vK1h`IKB7k^xi`f z$H_mtr_+@M>C5+_xt%v}{#WO{86J83;VS@Ei3JLtp<*+hsY1oGzo z0?$?OJO$79;{|@aP!fO6t9TJ!?8i&|c&UPWRMbkwT3nEeFH`Yyyh6b%Rm^nBuTt@9 z+$&-4lf!G|@LCo3<8=yN@5dYbc%uq|Hz|0tiiLQKiUoM9g14zyECKGv0}3AWv2WJ zUAXGUhvkNk`0-H%ACsRSmy4fJ@kxBD3ZKSj6g(n1KPw?g{v19phcBr3BEF>J%lL|d zud3LNuL;cR*xS+;X+N^Br+x2{&hDMhb-$6_fKU(Pt0FQUXgNrZvzsVCnsFqv?#L z4-FYsQ-?D>;LdjHu_TT1CHN~aGkmDjWJkJg4G^!+V_APd%_48tErDv6BW5;ji^UDD zRu5Sw7wwplk`w{OGEKWJM&61c-AWn!SeUP8G#+beH4_Ov*)NUV?eGw&GHNDI6G(1Y zTfCv?T*@{QyK|!Q09wbk5koPD>=@(cA<~i4pSO?f(^5sSbdhUc+K$DW#_7^d7i%At z?KBg#vm$?P4h%?T=XymU;w*AsO_tJr)`+HUll+Uk_zx6vNw>G3jT){w3ck+Z=>7f0 zZVkM*!k^Z_E@_pZK6uH#|vzoL{-j1VFlUHP&5~q?j=UvJJNQG ztQdiCF$8_EaN_Pu8+afN6n8?m5UeR_p_6Log$5V(n9^W)-_vS~Ws`RJhQNPb1$C?| zd9D_ePe*`aI9AZ~Ltbg)DZ;JUo@-tu*O7CJ=T)ZI1&tn%#cisS85EaSvpS~c#CN9B z#Bx$vw|E@gm{;cJOuDi3F1#fxWZ9+5JCqVRCz5o`EDW890NUfNCuBn)3!&vFQE{E$L`Cf7FMSSX%ppLH+Z}#=p zSow$)$z3IL7frW#M>Z4|^9T!=Z8}B0h*MrWXXiVschEA=$a|yX9T~o!=%C?T+l^Cc zJx&MB$me(a*@lLLWZ=>PhKs!}#!ICa0! zq%jNgnF$>zrBZ3z%)Y*yOqHbKzEe_P=@<5$u^!~9G2OAzi#}oP&UL9JljG!zf{JIK z++G*8j)K=$#57N)hj_gSA8golO7xZP|KM?elUq)qLS)i(?&lk{oGMJh{^*FgklBY@Xfl<_Q zXP~(}ST6V01$~VfOmD6j!Hi}lsE}GQikW1YmBH)`f_+)KI!t#~B7=V;{F*`umxy#2Wt8(EbQ~ks9wZS(KV5#5Tn3Ia90r{}fI%pfbqBAG zhZ)E7)ZzqA672%@izC5sBpo>dCcpXi$VNFztSQnmI&u`@zQ#bqFd9d&ls?RomgbSh z9a2rjfNiKl2bR!$Y1B*?3Ko@s^L5lQN|i6ZtiZL|w5oq%{Fb@@E*2%%j=bcma{K~9 z*g1%nEZ;0g;S84ZZ$+Rfurh;Nhq0;{t~(EIRt}D@(Jb7fbe+_@H=t&)I)gPCtj*xI z9S>k?WEAWBmJZ|gs}#{3*pR`-`!HJ)1Dkx8vAM6Tv1bHZhH=MLI;iC#Y!$c|$*R>h zjP{ETat(izXB{@tTOAC4nWNhh1_%7AVaf!kVI5D=Jf5I1!?}stbx_Yv23hLf$iUTb z-)WrTtd2X+;vBW_q*Z6}B!10fs=2FA=3gy*dljsE43!G*3Uw(Is>(-a*5E!T4}b-Y zfvOC)-HYjNfcpi`=kG%(X3XcP?;p&=pz+F^6LKqRom~pA}O* zitR+Np{QZ(D2~p_Jh-k|dL!LPmexLM?tEqI^qRDq9Mg z5XBftj3z}dFir4oScbB&{m5>s{v&U=&_trq#7i&yQN}Z~OIu0}G)>RU*`4<}@7bB% zKYxGx0#L#u199YKSWZwV$nZd>D>{mDTs4qDNyi$4QT6z~D_%Bgf?>3L#NTtvX;?2D zS3IT*2i$Snp4fjDzR#<)A``4|dA(}wv^=L?rB!;kiotwU_gma`w+@AUtkSyhwp{M} z!e`jbUR3AG4XvnBVcyIZht6Vi~?pCC!$XF2 z*V~)DBVm8H7$*OZQJYl3482hadhsI2NCz~_NINtpC?|KI6H3`SG@1d%PsDdw{u}hq zN;OU~F7L1jT&KAitilb&Fl3X12zfSuFm;X)xQWOHL&7d)Q5wgn{78QJ6k5J;is+XP zCPO8_rlGMJB-kuQ*_=Yo1TswG4xnZd&eTjc8=-$6J^8TAa~kEnRQ@Zp-_W&B(4r@F zA==}0vBzsF1mB~743XqBmL9=0RSkGn$cvHf*hyc{<2{@hW+jKjbC|y%CNupHY_NC% zivz^btBLP-cDyV8j>u)=loBs>HoI5ME)xg)oK-Q0wAy|8WD$fm>K{-`0|W{H00;;G z000j`0OWQ8aHA9e04^;603eeQIvtaXMG=2tcr1y8Fl-J;AS+=<0%DU8Bp3oEEDhA^ zOY)M8%o5+cF$rC?trfMcty*f)R;^v=f~}||Xe!#;T3eTDZELN&-50xk+J1heP5AQ>h5O#S_uO;O@;~REd*_G$x$hVeE#bchX)otXQy|S5(oB)2a2%Sc(iDHm z=d>V|a!BLp9^#)o7^EQ2kg=K4%nI^sK2w@-kmvB+ARXYdq?xC2age6)e4$^UaY=wn zgLD^{X0A+{ySY+&7RpldwpC6=E zSPq?y(rl8ZN%(A*sapd4PU+dIakIwT0=zxIJEUW0kZSo|(zFEWdETY*ZjIk9uNMUA ze11=mHu8lUUlgRx!hItf0dAF#HfdIB+#aOuY--#QN9Ry zbx|XkG?PrBb@l6Owl{9Oa9w{x^R}%GwcEEfY;L-6OU8|9RXvu`-ECS`jcO1x1MP{P zcr;Bw##*Dod9K@pEx9z9G~MiNi>8v1OU-}vk*HbI)@CM? zn~b=jWUF%HP=CS+VCP>GiAU_UOz$aq3%%Z2laq^Gx`WAEmuNScCN)OlW>YHGYFgV2 z42lO5ZANs5VMXLS-RZTvBJkWy*OeV#L;7HwWg51*E|RpFR=H}h(|N+79g)tIW!RBK ze08bg^hlygY$C2`%N>7bDm`UZ(5M~DTanh3d~dg+OcNdUanr8azO?})g}EfnUB;5- zE1FX=ru?X=zAk4_6@__o1fE+ml1r&u^f1Kb24Jf-)zKla%-dbd>UZ1 zrj3!RR!Jg`ZnllKJ)4Yfg)@z>(fFepeOcp=F-^VHv?3jSxfa}-NB~*qkJ5Uq(yn+( z<8)qbZh{C!xnO@-XC~XMNVnr-Z+paowv!$H7>`ypMwA(X4(knx7z{UcWWe-wXM!d? zYT}xaVy|7T@yCbNOoy)$D=E%hUNTm(lPZqL)?$v+-~^-1P8m@Jm2t^L%4#!JK#Vtg zyUjM+Y*!$);1<)0MUqL00L0*EZcsE&usAK-?|{l|-)b7|PBKl}?TM6~#j9F+eZq25_L&oSl}DOMv^-tacpDI)l*Ws3u+~jO@;t(T)P=HCEZ#s_5q=m zOsVY!QsOJn)&+Ge6Tm)Ww_Bd@0PY(78ZJ)7_eP-cnXYk`>j9q`x2?Xc6O@55wF+6R zUPdIX!2{VGA;FSivN@+;GNZ7H2(pTDnAOKqF*ARg+C54vZ@Ve`i?%nDDvQRh?m&`1 zq46gH)wV=;UrwfCT3F(m!Q5qYpa!#f6qr0wF=5b9rk%HF(ITc!*R3wIFaCcftGwPt z(kzx{$*>g5L<;u}HzS4XD%ml zmdStbJcY@pn`!fUmkzJ8N>*8Y+DOO^r}1f4ix-`?x|khoRvF%jiA)8)P{?$8j2_qN zcl3Lm9-s$xdYN9)>3j6BPFK)Jbovl|Sf_p((CHe!4hx@F)hd&&*Xb&{TBj>%pT;-n z{3+hA^QZYnjXxtF2XwxPZ`S#J8h>5qLwtwM-{5abbEnRS z`9_`Zq8FJiI#0syE_V_3M&trw$P=ezkHosV$8&I5c0(*-9KBE5DJOC-Xv zw}1bq~AD0_Xerm`%ryiG9_$S z5G|btfiAUNdV09SO2l9v+e#(H6HYOdQs=^ z@xwZQU)~;p1L*~ciC}9ao{nQ-@B>rpUzKBxv=cUusOP5Trs3QnvHxGh9e>s7AM{V1|HfYe z3QwH;nHHR49fYzuGc3W3l5xrDAI392SFXx>lWE3V9Ds9il3PyZaN5>oC3>9W-^7vC z3~KZ-@iD?tIkhg+6t{m;RGk2%>@I0&kf)o$+-^ls0(YABNbM(=l#ad@nKp_j=b~Xs ziR;xu_+)lxy6|+af!@}gO2H_x)p;nZ-tYxW5Omq=l`GzMp*GTLr>vZN1?e}^C$t*Z zvzEdIc2|HA2RFN_4#EkzMqKnbbw!?!?%B@M0^^5Z;K?x-%lg?Z>}wMV8zEqHZ$cr~Y#Wv>9+)KMUZatUqbRU8 z8t9qrek(H^C0Tuzq|cP2$WL7tzj+Dj5y^2SF1D154CnsB$xbz`$wV||n-cG%rsT$p z+3RHdadK(3-noj(2L#8c5lODg)V8pv(GEnNb@F>dEHQr>!qge@L>#qg)RAUtiOYqF ziiV_ETExwD)bQ<))?-9$)E(FiRBYyC@}issHS!j9n)~I1tarxnQ2LfjdIJ)*jp{0E z&1oTd%!Qbw$W58s!6ms>F z=p0!~_Mv~8jyaicOS*t(ntw`5uFi0Bc4*mH8kSkk$>!f0;FM zX_t14I55!ZVsg0O$D2iuEDb7(J>5|NKW^Z~kzm@dax z9(|As$U7^}LF%#`6r&UPB*6`!Rf74h~*C=ami6xUxYCwiJxdr$+`z zKSC4A%8!s%R&j*2si(OEc*fy!q)?%=TjDZJ2}O zxT6o>jlKXz_7_Y$N})}IG`*#KfMzs#R(SI#)3*ZEzCv%_tu(VTZ5J| zw2$5kK)xTa>xGFgS0?X(NecjzFVKG%VVn?neu=&eQ+DJ1APlY1E?Q1s!Kk=yf7Uho z>8mg_!U{cKqpvI3ucSkC2V`!d^XMDk;>GG~>6>&X_z75-kv0UjevS5ORHV^e8r{tr z-9z*y&0eq3k-&c_AKw~<`8dtjsP0XgFv6AnG?0eo5P14T{xW#b*Hn2gEnt5-KvN1z zy!TUSi>IRbD3u+h@;fn7fy{F&hAKx7dG4i!c?5_GnvYV|_d&F16p;)pzEjB{zL-zr z(0&AZUkQ!(A>ghC5U-)t7(EXb-3)tNgb=z`>8m8n+N?vtl-1i&*ftMbE~0zsKG^I$ zSbh+rUiucsb!Ax@yB}j>yGeiKIZk1Xj!i#K^I*LZW_bWQIA-}FmJ~^}>p=K$bX9F{}z{s^KWc~OK(zl_X57aB^J9v}yQ5h#BE$+C)WOglV)nd0WWtaF{7`_Ur`my>4*NleQG#xae4fIo(b zW(&|g*#YHZNvDtE|6}yHvu(hDekJ-t*f!2RK;FZHRMb*l@Qwkh*~CqQRNLaepXypX z1?%ATf_nHIu3z6gK<7Dmd;{`0a!|toT0ck|TL$U;7Wr-*piO@R)KrbUz8SXO0vr1K z>76arfrqImq!ny+VkH!4?x*IR$d6*;ZA}Mhro(mzUa?agrFZpHi*)P~4~4N;XoIvH z9N%4VK|j4mV2DRQUD!_-9fmfA2(YVYyL#S$B;vqu7fnTbAFMqH``wS7^B5=|1O&fL z)qq(oV6_u4x(I(**#mD}MnAy(C&B4a1n6V%$&=vrIDq^F_KhE5Uw8_@{V`_#M0vCu zaNUXB=n0HT@D+ppDXi8-vp{tj)?7+k>1j}VvEKRgQ~DWva}8*pp`W8~KRo*kJ*&X} zP!~2fxQr@dM*q0dI|)Fux=pZWBk==RI7i{^BQf`kWlD2%|@R9!JA7& zLbM$uJ12y}_62$|T|{)@OJZtzfpL^t@1nMTYHutrF#D+^?~CN~9`YQ@#&&@c_Zf)( zbC~y8!2LO8jHwQXv>G~1q?c68ipT*%dY&c{8wd_!Y#~tMJ7yk!F8| zt?m_CLVw6cU@@p(#h4cY&Qsfz2Xp3w^4Cg%m03Tmq~9n%hyoMH^KY7{(QkRyn_!YB zzZa!Tgr~5$MAG$x)Fs71#6j}Kvcv3=9VUX8CH< zbP3|fY8f#$K*<5JQ7whM(v=GN2k26Xsh)#0!HKS(koLgAp-;)8z0w&_Z=nG4v6n8u z&Tm0Fi){4_!Y5Kp?!zv$FKfUifQ{%c82uYfrvE{%ejUd72aNYmI*0z3-a-EYr+bB->oH3#t(AY3 zV{Z=(SJr;D#0(`u*dc*~9T7D8Pudw894%!>c4wU&V1m<~0InidR6fbi?yPl(z+sKa zdF*kS>_4^1UO>y4T%Ar>epSr5&vp`$KdY7B(F%P0@VyHk@1fJ=6X0=aGjD-)BrOJD zW}IU@hg~^2r>a1fQvjTtvL*mKJ7q;pfP*U2=URL`VB_Y_JojbZ+MS=vaVN0C6L_MV zG1#5=35-E`KsD%r>-Q_ndvJ2tOYcMMP9f*t0iJ`(Z`^+YP)h>@lR(@Wvrt-`0tHG+ zuP2R@@mx=T@fPoQ1s`e^1I0H*kQPBGDky@!ZQG@8jY-+2ihreG5q$6i{3vmDTg0j$ zzRb*-nKN@{_wD`V6+i*YS)?$XfrA-sW?js?SYU8#vXxxQCc|*K!EbpWfu)3~jwq6_@KC0m;3A%jH^18_a0;ksC2DEwa@2{9@{ z9@T??<4QwR69zk{UvcHHX;`ICOwrF;@U;etd@YE)4MzI1WCsadP=`%^B>xPS-{`=~ zZ+2im8meb#4p~XIL9}ZOBg7D8R=PC8V}ObDcxEEK(4yGKcyCQWUe{9jCs+@k!_y|I z%s{W(&>P4w@hjQ>PQL$zY+=&aDU6cWr#hG)BVCyfP)h>@3IG5I2mk;8K>)Ppba*!h z005B=001VF5fT=Y4_ytCUk`sv8hJckqSy&Gc2Jx^WJ$J~08N{il-M$fz_ML$)Cpil z(nOv_nlZB^c4s&&O3h=OLiCz&(|f0 zxWU_-JZy>hxP*gvR>CLnNeQ1~g;6{g#-}AbkIzWR;j=8=6!AHpKQCbjFYxf9h%bov zVi;eNa1>t-<14KERUW>^KwoF+8zNo`Y*WiQwq}3m0_2RYtL9Wmu`JaRaQMQ)`Si^6+VbM`!rH~T?DX2=(n4nT zf`G`(Rpq*pDk*v~wMYPZ@vMNZDMPnxMYmU!lA{Xfo?n=Ibb4y3eyY1@Dut4|Y^ml& zqs$r}jAo=B(Ml>ogeEjyv(E`=kBzPf2uv9TQtO$~bamD#=Tv`lNy(K|w$J2O6jS51 zzZtOCHDWz7W0=L1XDW5WR5mtLGc~W+>*vX5{e~U@rE~?7e>vKU-v8bj;F4#abtcV(3ZtwXo9ia93HiETyQXwW4a-0){;$OU*l` zW^bjkyZTJ6_DL^0}`*)#EZ|2nvKRzMLH9-~@Z6$v#t8Dm%(qpP+DgzNe6d)1q zBqhyF$jJTyYFvl_=a>#I8jhJ)d6SBNPg#xg2^kZ3NX8kQ74ah(Y5Z8mlXyzTD&}Q8 ziY(pj-N-V2f>&hZQJ`Di%wp2fN(I%F@l)3M8GcSdNy+#HuO{$I8NXubRlFkL)cY@b z#`v{}-^hRXEq*8B_cG=%PZvI$eo(|8Wc(2o8L#0_GX9L$1@yV>%7mGk)QTD1R*OvS z4OW;ym1)%k9Bfem0tOqq3yyAUWp&q|LsN!RDnxa|j;>R|Mm2rIv7=tej5GFaa+`#| z;7u9Z_^XV+vD@2hF8Xe63+Qd`oig6S9jX(*DbjzPb*K-H7c^7E-(~!R6E%TrgW;RvG;WS{Ziv*W*a*`9Bb;$Er3?MyF~5GcXv`k>U)n}lwv$Sp+H@IKA5$mKk0g*4Ln{!tfvITeY zzr%8JJ5BdcEYsR9eGzJ4B&$}4FMmbRU6{8{_w7Kl77@PNe7|Bc#c?5(C5&Z=kJ#(oM90D4`rh2S!|^L!P#e#1hkD5@~-- z`63GV0~*rOZSqw7k^#-Y$Q4z3Oa2SPRURqEahB1B^h{7~+p03SwzqL9QU#$3-X zdYtQ?-K5xDAdfomEd6(yPtZ!yY_<35bMedeq`z2JWorljz5-f9<^93HM-$#+acw%9r!JOM%O<|BR`W& zd-%j_?b^q7Kl6{q^N{cg2u;11rFB5EP+oqG9&pHD#_Mo@aNMj;LUvsl&nK(ca(hT( zzFc2oHC6WQv8g7jo+3ZSwK+9G$cvfRnql)?g=XeQ3+LTh3)79nhEle8OqS3T$qn(> z(=5Bg?EWq-ldEywgzXW965%H(9^ik*rH(8dNdkbcS9|ow&_r`X~R^R?B+(oTiMzzlx8KnHqUi z8Rh-)VAnS-CO+3}yxqm8)X+N+uzieFVm-F#syP#M1p5&$wX3MJ8 z+R@grZ*5G^Uh4I@VT=>C4RJNc^~3mx$kS1F{L?3)BzdduD2MZKdu#jNno&f2&d{?` zW(>$oktzY@GO{|Ln~Bt^A4)(%?l-&(Dm!iL#$K_xOyhwAf=K2<+Bom zw7|hl6E5}B$d%n0sfZvfQRy9Fyz2~ z83#=#LaHnf1th^k*p|ux8!!8pfHE!)x*%=_hAddl)P%4h4%&8!5-W#xqqb}c=H(i|wqcIS&oDQ{ zhI7N-$f$ra3=RjPmMh?-IEkJYQ<}R9Z!}wmp$#~Uc%u1oh#TP}wF*kJJmQX2#27kL z_dz(yKufo<=m71bZfLp^Ll#t3(IHkrgMcvx@~om%Ib(h(<$Da7urTI`x|%`wD--sN zJEEa>4DGSEG?0ulkosfj8IMNN4)B=ZtvGG{|4Fp=Xhg!wPNgYzS>{Bp%%Qa+624X@ X49Luk)baa85H9$5YCsTPT`SVRWMtMW diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2a563242c..ffed3a254 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0c8..1b6c78733 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/nukkit/build.gradle b/nukkit/build.gradle index a8c3a3aa1..df34188f0 100644 --- a/nukkit/build.gradle +++ b/nukkit/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.github.johnrengelman.shadow' version '2.0.4' + id 'com.github.johnrengelman.shadow' version '7.0.0' } repositories { @@ -8,14 +8,14 @@ repositories { } dependencies { - compile project(':common') + implementation project(':common') compileOnly project(':common:loader-utils') compileOnly 'cn.nukkit:nukkit:1.0-SNAPSHOT' } shadowJar { - archiveName = 'luckperms-nukkit.jarinjar' + archiveFileName = 'luckperms-nukkit.jarinjar' dependencies { include(dependency('me.lucko.luckperms:.*')) diff --git a/nukkit/loader/build.gradle b/nukkit/loader/build.gradle index a0dcb2bc6..cf2d0755e 100644 --- a/nukkit/loader/build.gradle +++ b/nukkit/loader/build.gradle @@ -10,19 +10,18 @@ repositories { dependencies { compileOnly 'cn.nukkit:nukkit:1.0-SNAPSHOT' - compile project(':api') - compile project(':common:loader-utils') + implementation project(':api') + implementation project(':common:loader-utils') } processResources { - from(sourceSets.main.resources.srcDirs) { - expand 'pluginVersion': project.ext.fullVersion - include 'plugin.yml' + filesMatching('plugin.yml') { + expand 'version': project.ext.fullVersion } } shadowJar { - archiveName = "LuckPerms-Nukkit-${project.ext.fullVersion}.jar" + archiveFileName = "LuckPerms-Nukkit-${project.ext.fullVersion}.jar" from { project(':nukkit').tasks.shadowJar.archiveFile @@ -31,4 +30,4 @@ shadowJar { artifacts { archives shadowJar -} \ No newline at end of file +} diff --git a/nukkit/loader/src/main/resources/plugin.yml b/nukkit/loader/src/main/resources/plugin.yml index 0ab32a8b2..cf2c48e00 100644 --- a/nukkit/loader/src/main/resources/plugin.yml +++ b/nukkit/loader/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LuckPerms -version: ${pluginVersion} +version: @pluginVersion@ api: ["1.0.5"] description: A permissions plugin author: Luck diff --git a/sponge/build.gradle b/sponge/build.gradle index 76d45f106..67038ca9f 100644 --- a/sponge/build.gradle +++ b/sponge/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'net.kyori.blossom' version '1.1.0' - id 'com.github.johnrengelman.shadow' version '2.0.4' + id 'net.kyori.blossom' version '1.3.0' + id 'com.github.johnrengelman.shadow' version '7.0.0' } repositories { @@ -8,10 +8,10 @@ repositories { } dependencies { - compile project(':common') - compile project(':sponge:sponge-service') - compile project(':sponge:sponge-service-api6') - compile project(':sponge:sponge-service-api7') + implementation project(':common') + implementation project(':sponge:sponge-service') + implementation project(':sponge:sponge-service-api6') + implementation project(':sponge:sponge-service-api7') compileOnly('org.spongepowered:spongeapi:7.3.0') { exclude(module: 'configurate-core') @@ -33,7 +33,7 @@ blossom { } shadowJar { - archiveName = "LuckPerms-Sponge-${project.ext.fullVersion}.jar" + archiveFileName = "LuckPerms-Sponge-${project.ext.fullVersion}.jar" dependencies { include(dependency('net.luckperms:.*')) diff --git a/sponge/sponge-service-api6/build.gradle b/sponge/sponge-service-api6/build.gradle index e714b63aa..d423d5f03 100644 --- a/sponge/sponge-service-api6/build.gradle +++ b/sponge/sponge-service-api6/build.gradle @@ -3,8 +3,8 @@ repositories { } dependencies { - compile project(':common') - compile project(':sponge:sponge-service') + implementation project(':common') + implementation project(':sponge:sponge-service') compileOnly('org.spongepowered:spongeapi:6.0.0') { exclude(module: 'configurate-core') diff --git a/sponge/sponge-service-api7/build.gradle b/sponge/sponge-service-api7/build.gradle index dadbbb378..a3fab4305 100644 --- a/sponge/sponge-service-api7/build.gradle +++ b/sponge/sponge-service-api7/build.gradle @@ -3,8 +3,8 @@ repositories { } dependencies { - compile project(':common') - compile project(':sponge:sponge-service') + implementation project(':common') + implementation project(':sponge:sponge-service') compileOnly('org.spongepowered:spongeapi:7.3.0') { exclude(module: 'configurate-core') diff --git a/sponge/sponge-service/build.gradle b/sponge/sponge-service/build.gradle index 6a31118e2..88c3f2e04 100644 --- a/sponge/sponge-service/build.gradle +++ b/sponge/sponge-service/build.gradle @@ -3,7 +3,7 @@ repositories { } dependencies { - compile project(':common') + implementation project(':common') compileOnly('org.spongepowered:spongeapi:7.2.0') { exclude(module: 'configurate-core') diff --git a/velocity/build.gradle b/velocity/build.gradle index 17ebab6e2..7eb45514e 100644 --- a/velocity/build.gradle +++ b/velocity/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'net.kyori.blossom' version '1.1.0' - id 'com.github.johnrengelman.shadow' version '2.0.4' + id 'net.kyori.blossom' version '1.3.0' + id 'com.github.johnrengelman.shadow' version '7.0.0' } repositories { @@ -8,7 +8,7 @@ repositories { } dependencies { - compile project(':common') + implementation project(':common') compileOnly 'com.velocitypowered:velocity-api:3.0.0' annotationProcessor 'com.velocitypowered:velocity-api:3.0.0' @@ -20,7 +20,7 @@ blossom { } shadowJar { - archiveName = "LuckPerms-Velocity-${project.ext.fullVersion}.jar" + archiveFileName = "LuckPerms-Velocity-${project.ext.fullVersion}.jar" dependencies { include(dependency('net.luckperms:.*')) From 93683c3a2f55c20bd37c6a7eb28aa088a130f8c8 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 20 Aug 2021 18:46:39 +0100 Subject: [PATCH 022/314] Update Fabric to 1.17, fix version placeholder filtering --- bukkit-legacy/loader/build.gradle | 2 +- .../loader/src/main/resources/plugin.yml | 2 +- bukkit/build.gradle | 3 +-- bukkit/loader/build.gradle | 2 +- bukkit/loader/src/main/resources/plugin.yml | 2 +- bungee/loader/build.gradle | 2 +- bungee/loader/src/main/resources/plugin.yml | 2 +- fabric/build.gradle | 17 ++++++----------- .../luckperms/fabric/FabricSenderFactory.java | 2 +- .../luckperms/fabric/LPFabricBootstrap.java | 4 ++-- nukkit/loader/build.gradle | 2 +- nukkit/loader/src/main/resources/plugin.yml | 2 +- 12 files changed, 18 insertions(+), 24 deletions(-) diff --git a/bukkit-legacy/loader/build.gradle b/bukkit-legacy/loader/build.gradle index a02a45bf5..0aa4c7323 100644 --- a/bukkit-legacy/loader/build.gradle +++ b/bukkit-legacy/loader/build.gradle @@ -15,7 +15,7 @@ dependencies { processResources { filesMatching('plugin.yml') { - expand 'version': project.ext.fullVersion + expand 'pluginVersion': project.ext.fullVersion } } diff --git a/bukkit-legacy/loader/src/main/resources/plugin.yml b/bukkit-legacy/loader/src/main/resources/plugin.yml index c892df5c7..d0d010212 100644 --- a/bukkit-legacy/loader/src/main/resources/plugin.yml +++ b/bukkit-legacy/loader/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LuckPerms -version: @pluginVersion@ +version: ${pluginVersion} description: A permissions plugin author: Luck website: https://luckperms.net diff --git a/bukkit/build.gradle b/bukkit/build.gradle index b84107fbd..c040822ff 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -1,6 +1,5 @@ plugins { id 'com.github.johnrengelman.shadow' version '7.0.0' - id 'java' } repositories { @@ -18,7 +17,7 @@ dependencies { exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') } - compileOnly 'me.lucko:commodore:1.9' + compileOnly 'me.lucko:commodore:1.10' compileOnly('net.milkbowl.vault:VaultAPI:1.7') { exclude(module: 'bukkit') } diff --git a/bukkit/loader/build.gradle b/bukkit/loader/build.gradle index 564829b37..52d01ecf8 100644 --- a/bukkit/loader/build.gradle +++ b/bukkit/loader/build.gradle @@ -15,7 +15,7 @@ dependencies { processResources { filesMatching('plugin.yml') { - expand 'version': project.ext.fullVersion + expand 'pluginVersion': project.ext.fullVersion } } diff --git a/bukkit/loader/src/main/resources/plugin.yml b/bukkit/loader/src/main/resources/plugin.yml index adbbb3fd2..708025aee 100644 --- a/bukkit/loader/src/main/resources/plugin.yml +++ b/bukkit/loader/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LuckPerms -version: @pluginVersion@ +version: ${pluginVersion} description: A permissions plugin author: Luck website: https://luckperms.net diff --git a/bungee/loader/build.gradle b/bungee/loader/build.gradle index 014465fb5..10de5e9e5 100644 --- a/bungee/loader/build.gradle +++ b/bungee/loader/build.gradle @@ -11,7 +11,7 @@ dependencies { processResources { filesMatching('plugin.yml') { - expand 'version': project.ext.fullVersion + expand 'pluginVersion': project.ext.fullVersion } } diff --git a/bungee/loader/src/main/resources/plugin.yml b/bungee/loader/src/main/resources/plugin.yml index 58e7f88a9..7b71717d3 100644 --- a/bungee/loader/src/main/resources/plugin.yml +++ b/bungee/loader/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LuckPerms -version: @pluginVersion@ +version: ${pluginVersion} description: A permissions plugin author: Luck main: me.lucko.luckperms.bungee.loader.BungeeLoaderPlugin diff --git a/fabric/build.gradle b/fabric/build.gradle index 85b9e2f52..24672bd2f 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -2,7 +2,7 @@ import net.fabricmc.loom.task.RemapJarTask plugins { id 'com.github.johnrengelman.shadow' version '7.0.0' - id 'fabric-loom' version '0.6-SNAPSHOT' + id 'fabric-loom' version '0.9.+' } archivesBaseName = 'luckperms' @@ -12,16 +12,11 @@ repositories { mavenLocal() } -def minecraftVersion = '1.16.4' -def yarnBuild = 7 -def loaderVersion = '0.10.8' -def fabricApiVersion = '0.28.4+1.16' - dependencies { - // Fabric Stuff, We don't specifically target only a single version but yarn mappings require a version to be specified. - minecraft "com.mojang:minecraft:${minecraftVersion}" - mappings "net.fabricmc:yarn:${minecraftVersion}+build.${yarnBuild}:v2" - modImplementation "net.fabricmc:fabric-loader:${loaderVersion}" + // https://modmuss50.me/fabric.html + minecraft 'com.mojang:minecraft:1.17.1' + mappings 'net.fabricmc:yarn:1.17.1+build.39:v2' + modImplementation 'net.fabricmc:fabric-loader:0.11.6' Set apiModules = [ 'fabric-api-base', @@ -31,7 +26,7 @@ dependencies { ] apiModules.forEach { - modImplementation(fabricApi.module(it, fabricApiVersion)) + modImplementation(fabricApi.module(it, '0.38.0+1.17')) } include(modImplementation('me.lucko:fabric-permissions-api:0.1-SNAPSHOT')) diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSenderFactory.java b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSenderFactory.java index 5b5127d67..94f53d1b7 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSenderFactory.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSenderFactory.java @@ -101,7 +101,7 @@ protected boolean hasPermission(ServerCommandSource commandSource, String node) @Override protected void performCommand(ServerCommandSource sender, String command) { - sender.getMinecraftServer().getCommandManager().execute(sender, command); + sender.getServer().getCommandManager().execute(sender, command); } @Override diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricBootstrap.java b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricBootstrap.java index 215b37381..0f2423945 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricBootstrap.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricBootstrap.java @@ -230,13 +230,13 @@ public Optional getPlayer(UUID uniqueId) { @Override public Optional lookupUniqueId(String username) { - return getServer().map(MinecraftServer::getUserCache).map(c -> c.findByName(username)).map(GameProfile::getId); + return getServer().map(MinecraftServer::getUserCache).flatMap(c -> c.findByName(username)).map(GameProfile::getId); } @Override public Optional lookupUsername(UUID uniqueId) { - return getServer().map(MinecraftServer::getUserCache).map(c -> c.getByUuid(uniqueId)).map(GameProfile::getName); + return getServer().map(MinecraftServer::getUserCache).flatMap(c -> c.getByUuid(uniqueId)).map(GameProfile::getName); } @Override diff --git a/nukkit/loader/build.gradle b/nukkit/loader/build.gradle index cf2d0755e..953463948 100644 --- a/nukkit/loader/build.gradle +++ b/nukkit/loader/build.gradle @@ -16,7 +16,7 @@ dependencies { processResources { filesMatching('plugin.yml') { - expand 'version': project.ext.fullVersion + expand 'pluginVersion': project.ext.fullVersion } } diff --git a/nukkit/loader/src/main/resources/plugin.yml b/nukkit/loader/src/main/resources/plugin.yml index cf2c48e00..0ab32a8b2 100644 --- a/nukkit/loader/src/main/resources/plugin.yml +++ b/nukkit/loader/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LuckPerms -version: @pluginVersion@ +version: ${pluginVersion} api: ["1.0.5"] description: A permissions plugin author: Luck From a182768cfa8217e4c9da701b179641c918a2d182 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 25 Aug 2021 19:30:46 +0100 Subject: [PATCH 023/314] Change user housekeeper to be more lenient towards API usage (#3143) --- .../api/implementation/ApiUserManager.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiUserManager.java b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiUserManager.java index ff1649f69..2be57b78a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiUserManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiUserManager.java @@ -63,17 +63,20 @@ protected net.luckperms.api.model.user.User proxy(User internal) { return internal == null ? null : internal.getApiProxy(); } + private net.luckperms.api.model.user.User proxyAndRegisterUsage(User internal) { + if (internal != null) { + this.plugin.getUserManager().getHouseKeeper().registerApiUsage(internal.getUniqueId()); + } + return proxy(internal); + } + @Override public @NonNull CompletableFuture loadUser(@NonNull UUID uniqueId, @Nullable String username) { Objects.requireNonNull(uniqueId, "uuid"); ApiUtils.checkUsername(username, this.plugin); - if (this.plugin.getUserManager().getIfLoaded(uniqueId) == null) { - this.plugin.getUserManager().getHouseKeeper().registerApiUsage(uniqueId); - } - return this.plugin.getStorage().loadUser(uniqueId, username) - .thenApply(this::proxy); + .thenApply(this::proxyAndRegisterUsage); } @Override @@ -154,13 +157,13 @@ protected net.luckperms.api.model.user.User proxy(User internal) { @Override public net.luckperms.api.model.user.User getUser(@NonNull UUID uniqueId) { Objects.requireNonNull(uniqueId, "uuid"); - return proxy(this.handle.getIfLoaded(uniqueId)); + return proxyAndRegisterUsage(this.handle.getIfLoaded(uniqueId)); } @Override public net.luckperms.api.model.user.User getUser(@NonNull String username) { Objects.requireNonNull(username, "name"); - return proxy(this.handle.getByUsername(username)); + return proxyAndRegisterUsage(this.handle.getByUsername(username)); } @Override From 062fca3f75493512d10c94562c0692a8e683bb34 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 25 Aug 2021 19:36:53 +0100 Subject: [PATCH 024/314] Fix Fabric version placeholder in metadata file (#3141) --- fabric/src/main/resources/fabric.mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 1dcabbce3..8a32a743b 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "luckperms", - "version": "@version@", + "version": "${version}", "name": "LuckPerms", "icon": "assets/luckperms/icon.png", "description": "A permissions plugin (mod)", From 9cd33d26f276b255d065d17cf471c4db0b78f6ed Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 25 Aug 2021 20:09:20 +0100 Subject: [PATCH 025/314] Fix toLowerCase/toUpperCase locale sensitivity --- .../net/luckperms/api/node/ChatMetaType.java | 4 +++- .../node/metadata/SimpleNodeMetadataKey.java | 3 ++- .../luckperms/api/query/SimpleOptionKey.java | 3 ++- .../LuckPermsPermissionAttachment.java | 5 ++-- .../inject/server/LuckPermsDefaultsMap.java | 3 ++- .../inject/server/LuckPermsPermissionMap.java | 3 ++- .../listeners/BukkitPlatformListener.java | 7 +++--- .../bukkit/vault/LuckPermsVaultChat.java | 5 ++-- .../vault/LuckPermsVaultPermission.java | 3 ++- .../common/actionlog/LoggedAction.java | 9 ++++---- .../lucko/luckperms/common/api/ApiUtils.java | 3 ++- .../common/api/implementation/ApiUser.java | 5 ++-- .../luckperms/common/backup/Importer.java | 3 ++- .../common/bulkupdate/query/QueryField.java | 4 +++- .../cacheddata/type/SimpleMetaCache.java | 5 ++-- .../type/SimpleMetaValueSelector.java | 3 ++- .../calculator/PermissionCalculator.java | 3 ++- .../common/command/CommandManager.java | 13 ++++++----- .../command/abstraction/ChildCommand.java | 3 ++- .../abstraction/GenericChildCommand.java | 3 ++- .../abstraction/GenericParentCommand.java | 7 +++--- .../command/abstraction/ParentCommand.java | 5 ++-- .../command/abstraction/SingleCommand.java | 3 ++- .../common/command/spec/CommandSpec.java | 3 ++- .../command/tabcomplete/TabCompleter.java | 3 ++- .../command/tabcomplete/TabCompletions.java | 3 ++- .../common/command/utils/ArgumentList.java | 5 ++-- .../common/command/utils/SortType.java | 4 +++- .../generic/meta/MetaAddChatMeta.java | 3 ++- .../generic/meta/MetaAddTempChatMeta.java | 3 ++- .../commands/generic/meta/MetaClear.java | 5 ++-- .../generic/meta/MetaRemoveChatMeta.java | 5 ++-- .../generic/meta/MetaRemoveTempChatMeta.java | 5 ++-- .../generic/meta/MetaSetChatMeta.java | 3 ++- .../generic/meta/MetaSetTempChatMeta.java | 3 ++- .../generic/parent/ParentClearTrack.java | 3 ++- .../generic/parent/ParentSetTrack.java | 3 ++- .../parent/UserSwitchPrimaryGroup.java | 5 ++-- .../common/commands/group/CreateGroup.java | 4 +++- .../common/commands/group/DeleteGroup.java | 3 ++- .../common/commands/group/GroupClone.java | 4 +++- .../commands/group/GroupParentCommand.java | 3 ++- .../common/commands/group/GroupRename.java | 3 ++- .../common/commands/log/LogGroupHistory.java | 3 ++- .../common/commands/log/LogTrackHistory.java | 3 ++- .../commands/misc/BulkUpdateCommand.java | 5 ++-- .../common/commands/misc/EditorCommand.java | 3 ++- .../common/commands/misc/VerboseCommand.java | 3 ++- .../common/commands/track/CreateTrack.java | 4 +++- .../common/commands/track/DeleteTrack.java | 3 ++- .../common/commands/track/TrackAppend.java | 3 ++- .../common/commands/track/TrackClone.java | 4 +++- .../common/commands/track/TrackInsert.java | 3 ++- .../commands/track/TrackParentCommand.java | 3 ++- .../common/commands/track/TrackRemove.java | 3 ++- .../common/commands/track/TrackRename.java | 4 +++- .../common/commands/user/UserDemote.java | 3 ++- .../common/commands/user/UserPromote.java | 3 ++- .../luckperms/common/config/ConfigKeys.java | 23 ++++++++++--------- .../config/generic/key/ConfigKeyFactory.java | 3 ++- .../common/context/WorldNameRewriter.java | 3 ++- .../contextset/AbstractContextSet.java | 5 ++-- .../common/dependencies/Dependency.java | 3 ++- .../common/messaging/MessagingFactory.java | 4 +++- .../metastacking/StandardStackElements.java | 3 ++- .../lucko/luckperms/common/model/Group.java | 3 ++- .../luckperms/common/model/HolderType.java | 4 +++- .../common/model/PermissionHolder.java | 5 ++-- .../common/model/PrimaryGroupHolder.java | 3 ++- .../luckperms/common/model/WeightCache.java | 3 ++- .../manager/group/AbstractGroupManager.java | 3 ++- .../manager/track/AbstractTrackManager.java | 4 +++- .../common/node/types/DisplayName.java | 3 ++- .../common/node/types/Inheritance.java | 7 +++--- .../luckperms/common/node/types/Meta.java | 7 +++--- .../luckperms/common/node/types/Prefix.java | 3 ++- .../luckperms/common/node/types/Suffix.java | 3 ++- .../luckperms/common/node/types/Weight.java | 3 ++- .../common/node/utils/NodeJsonSerializer.java | 3 ++- .../plugin/AbstractLuckPermsPlugin.java | 3 ++- .../luckperms/common/storage/Storage.java | 9 ++++---- .../implementation/file/FileUuidCache.java | 9 ++++---- .../implementation/mongodb/MongoStorage.java | 5 ++-- .../implementation/split/SplitStorage.java | 3 ++- .../implementation/sql/SqlStorage.java | 13 ++++++----- .../common/treeview/PermissionRegistry.java | 3 ++- .../luckperms/common/treeview/TreeView.java | 3 ++- .../common/util/DurationFormatter.java | 3 ++- .../luckperms/common/util/EnumNamer.java | 3 ++- .../common/verbose/event/MetaCheckEvent.java | 6 +++-- .../verbose/event/PermissionCheckEvent.java | 8 ++++--- .../common/verbose/event/VerboseEvent.java | 3 ++- .../luckperms/common/util/EnumNamerTest.java | 4 +++- .../context/NukkitPlayerCalculator.java | 3 ++- .../nukkit/inject/PermissionDefault.java | 3 ++- .../LuckPermsPermissionAttachment.java | 5 ++-- .../inject/server/LuckPermsDefaultsMap.java | 3 ++- .../inject/server/LuckPermsPermissionMap.java | 3 ++- .../listeners/NukkitPlatformListener.java | 7 +++--- .../proxy/api7/PermissionServiceProxy.java | 7 +++--- .../reference/SubjectReferenceFactory.java | 5 ++-- .../sponge/commands/PermissionSet.java | 4 +++- .../sponge/commands/SpongeCommandUtils.java | 3 ++- .../sponge/commands/SpongeParentCommand.java | 7 +++--- .../listeners/SpongePlatformListener.java | 4 +++- .../model/manager/SpongeGroupManager.java | 11 +++++---- .../sponge/service/LuckPermsService.java | 7 +++--- .../calculated/CalculatedSubjectData.java | 9 ++++---- .../PermissionHolderSubjectData.java | 3 ++- .../model/persisted/PersistedCollection.java | 9 ++++---- 110 files changed, 310 insertions(+), 184 deletions(-) diff --git a/api/src/main/java/net/luckperms/api/node/ChatMetaType.java b/api/src/main/java/net/luckperms/api/node/ChatMetaType.java index 87a85c177..0f73c912e 100644 --- a/api/src/main/java/net/luckperms/api/node/ChatMetaType.java +++ b/api/src/main/java/net/luckperms/api/node/ChatMetaType.java @@ -31,6 +31,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Locale; + /** * Represents a type of chat meta */ @@ -70,7 +72,7 @@ public enum ChatMetaType { private final NodeType> nodeType; ChatMetaType(NodeType> nodeType) { - this.name = nodeType.name().toLowerCase(); + this.name = nodeType.name().toLowerCase(Locale.ROOT); this.nodeType = nodeType; } diff --git a/api/src/main/java/net/luckperms/api/node/metadata/SimpleNodeMetadataKey.java b/api/src/main/java/net/luckperms/api/node/metadata/SimpleNodeMetadataKey.java index 44a00074f..6350538ae 100644 --- a/api/src/main/java/net/luckperms/api/node/metadata/SimpleNodeMetadataKey.java +++ b/api/src/main/java/net/luckperms/api/node/metadata/SimpleNodeMetadataKey.java @@ -27,6 +27,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Locale; import java.util.Objects; final class SimpleNodeMetadataKey implements NodeMetadataKey { @@ -34,7 +35,7 @@ final class SimpleNodeMetadataKey implements NodeMetadataKey { private final Class type; SimpleNodeMetadataKey(String name, Class type) { - this.name = name.toLowerCase(); + this.name = name.toLowerCase(Locale.ROOT); this.type = type; } diff --git a/api/src/main/java/net/luckperms/api/query/SimpleOptionKey.java b/api/src/main/java/net/luckperms/api/query/SimpleOptionKey.java index 1a5dccb67..6efc5e2ba 100644 --- a/api/src/main/java/net/luckperms/api/query/SimpleOptionKey.java +++ b/api/src/main/java/net/luckperms/api/query/SimpleOptionKey.java @@ -27,6 +27,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Locale; import java.util.Objects; final class SimpleOptionKey implements OptionKey { @@ -35,7 +36,7 @@ final class SimpleOptionKey implements OptionKey { private final int hashCode; SimpleOptionKey(String name, Class type) { - this.name = name.toLowerCase(); + this.name = name.toLowerCase(Locale.ROOT); this.type = type; this.hashCode = Objects.hash(this.name, this.type); // cache hashcode } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissionAttachment.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissionAttachment.java index 0a9f10ac0..db31a08b8 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissionAttachment.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissionAttachment.java @@ -46,6 +46,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -239,7 +240,7 @@ public void setPermission(@NonNull String name, boolean value) { Objects.requireNonNull(name, "name is null"); Preconditions.checkArgument(!name.isEmpty(), "name is empty"); - String permission = name.toLowerCase(); + String permission = name.toLowerCase(Locale.ROOT); Boolean previous = this.perms.put(permission, value); if (previous != null && previous == value) { @@ -264,7 +265,7 @@ public void unsetPermission(@NonNull String name) { Objects.requireNonNull(name, "name is null"); Preconditions.checkArgument(!name.isEmpty(), "name is empty"); - String permission = name.toLowerCase(); + String permission = name.toLowerCase(Locale.ROOT); Boolean previous = this.perms.remove(permission); if (previous == null) { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsDefaultsMap.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsDefaultsMap.java index b1d9b93d4..bcc77146a 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsDefaultsMap.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsDefaultsMap.java @@ -43,6 +43,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -140,7 +141,7 @@ private final class DefaultsCache extends Cache> { protected @NonNull Map supply() { Map builder = new HashMap<>(); for (Permission perm : LuckPermsDefaultsMap.this.get(this.op)) { - String name = perm.getName().toLowerCase(); + String name = perm.getName().toLowerCase(Locale.ROOT); builder.put(name, true); for (Map.Entry child : LuckPermsDefaultsMap.this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) { builder.putIfAbsent(child.getKey(), child.getValue()); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsPermissionMap.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsPermissionMap.java index bfd4f46cb..fb5c43039 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsPermissionMap.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/server/LuckPermsPermissionMap.java @@ -40,6 +40,7 @@ import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -180,7 +181,7 @@ private void resolveChildren(Map accumulator, Map> groups, Map get(Object k) { } String key = (String) k; - return super.get(key.toLowerCase()); + return super.get(key.toLowerCase(Locale.ROOT)); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java index 2aa48cdfd..f7ed17485 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java @@ -30,6 +30,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import java.util.List; +import java.util.Locale; import java.util.Map; public class SimpleMetaValueSelector implements MetaValueSelector { @@ -81,7 +82,7 @@ public String select(List values) { public static Strategy parse(String s) { try { - return Strategy.valueOf(s.replace('-', '_').toUpperCase()); + return Strategy.valueOf(s.replace('-', '_').toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { return null; } diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/PermissionCalculator.java b/common/src/main/java/me/lucko/luckperms/common/calculator/PermissionCalculator.java index d878b4095..1c1d73829 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculator/PermissionCalculator.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/PermissionCalculator.java @@ -35,6 +35,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import java.util.Collection; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -85,7 +86,7 @@ public TristateResult checkPermission(String permission, PermissionCheckEvent.Or public TristateResult apply(@NonNull String permission) { // convert the permission to lowercase, as all values in the backing map are also lowercase. // this allows fast case insensitive lookups - permission = permission.toLowerCase(); + permission = permission.toLowerCase(Locale.ROOT); // offer the permission to the permission vault // we only need to do this once per permission, so it doesn't matter diff --git a/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java b/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java index dbb9da189..e9d206227 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java @@ -75,6 +75,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; @@ -130,7 +131,7 @@ public CommandManager(LuckPermsPlugin plugin) { .add(new ListTracks()) .build() .stream() - .collect(ImmutableCollectors.toMap(c -> c.getName().toLowerCase(), Function.identity())); + .collect(ImmutableCollectors.toMap(c -> c.getName().toLowerCase(Locale.ROOT), Function.identity())); } public LuckPermsPlugin getPlugin() { @@ -235,7 +236,7 @@ private void execute(Sender sender, String label, List arguments) { } // Look for the main command. - Command main = this.mainCommands.get(arguments.get(0).toLowerCase()); + Command main = this.mainCommands.get(arguments.get(0).toLowerCase(Locale.ROOT)); // Main command not found if (main == null) { @@ -276,7 +277,7 @@ public List tabCompleteCommand(Sender sender, List arguments) { .collect(Collectors.toList()); return TabCompleter.create() - .at(0, CompletionSupplier.startsWith(() -> mains.stream().map(c -> c.getName().toLowerCase()))) + .at(0, CompletionSupplier.startsWith(() -> mains.stream().map(c -> c.getName().toLowerCase(Locale.ROOT)))) .from(1, partial -> mains.stream() .filter(m -> m.getName().equalsIgnoreCase(arguments.get(0))) .findFirst() @@ -333,7 +334,7 @@ private static void applyConvenienceAliases(List args, boolean rewriteLa // '/lp user Luck p set --> /lp user Luck permission set' etc // ^ ^^^^^^^^^^ if (args.size() >= 3 && (rewriteLastArgument || args.size() >= 4)) { - String arg0 = args.get(0).toLowerCase(); + String arg0 = args.get(0).toLowerCase(Locale.ROOT); if (arg0.equals("user") || arg0.equals("group")) { replaceArgs(args, 2, arg -> { switch (arg) { @@ -353,7 +354,7 @@ private static void applyConvenienceAliases(List args, boolean rewriteLa // '/lp user Luck permission i' --> '/lp user Luck permission info' etc // ^ ^^^^ if (args.size() >= 4 && (rewriteLastArgument || args.size() >= 5)) { - String arg2 = args.get(2).toLowerCase(); + String arg2 = args.get(2).toLowerCase(Locale.ROOT); if (arg2.equals("permission") || arg2.equals("parent") || arg2.equals("meta")) { replaceArgs(args, 3, arg -> arg.equals("i") ? "info" : null); } @@ -363,7 +364,7 @@ private static void applyConvenienceAliases(List args, boolean rewriteLa } private static void replaceArgs(List args, int i, Function rewrites) { - String arg = args.get(i).toLowerCase(); + String arg = args.get(i).toLowerCase(Locale.ROOT); String rewrite = rewrites.apply(arg); if (rewrite != null) { args.remove(i); diff --git a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ChildCommand.java b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ChildCommand.java index a3f6cc67e..d388ab0bf 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ChildCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ChildCommand.java @@ -36,6 +36,7 @@ import net.kyori.adventure.text.format.NamedTextColor; import java.util.List; +import java.util.Locale; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -58,7 +59,7 @@ public void sendUsage(Sender sender, String label) { TextComponent.Builder builder = Component.text() .append(Component.text('>', NamedTextColor.DARK_AQUA)) .append(Component.space()) - .append(Component.text(getName().toLowerCase(), NamedTextColor.GREEN)); + .append(Component.text(getName().toLowerCase(Locale.ROOT), NamedTextColor.GREEN)); if (getArgs().isPresent()) { List argUsages = getArgs().get().stream() diff --git a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericChildCommand.java b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericChildCommand.java index c92340c77..853af290e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericChildCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericChildCommand.java @@ -41,6 +41,7 @@ import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -106,7 +107,7 @@ public void sendUsage(Sender sender) { TextComponent.Builder builder = Component.text() .append(Component.text('>', NamedTextColor.DARK_AQUA)) .append(Component.space()) - .append(Component.text(getName().toLowerCase(), NamedTextColor.GREEN)); + .append(Component.text(getName().toLowerCase(Locale.ROOT), NamedTextColor.GREEN)); if (getArgs() != null) { List argUsages = getArgs().stream() diff --git a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericParentCommand.java b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericParentCommand.java index 510eee3cb..6b80a3305 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericParentCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericParentCommand.java @@ -38,6 +38,7 @@ import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.stream.Collectors; /** @@ -96,7 +97,7 @@ public List tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentL return TabCompleter.create() .at(0, CompletionSupplier.startsWith(() -> this.children.stream() .filter(s -> s.isAuthorized(sender, this.type)) - .map(s -> s.getName().toLowerCase()) + .map(s -> s.getName().toLowerCase(Locale.ROOT)) )) .from(1, partial -> this.children.stream() .filter(s -> s.isAuthorized(sender, this.type)) @@ -121,10 +122,10 @@ private void sendUsageDetailed(Sender sender, String label) { if (!subs.isEmpty()) { switch (this.type) { case USER: - Message.MAIN_COMMAND_USAGE_HEADER.send(sender, getName(), String.format("/%s user " + getName().toLowerCase(), label)); + Message.MAIN_COMMAND_USAGE_HEADER.send(sender, getName(), String.format("/%s user " + getName().toLowerCase(Locale.ROOT), label)); break; case GROUP: - Message.MAIN_COMMAND_USAGE_HEADER.send(sender, getName(), String.format("/%s group " + getName().toLowerCase(), label)); + Message.MAIN_COMMAND_USAGE_HEADER.send(sender, getName(), String.format("/%s group " + getName().toLowerCase(Locale.ROOT), label)); break; default: throw new AssertionError(this.type); diff --git a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ParentCommand.java b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ParentCommand.java index 0f41f74e6..cbde0a9c8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ParentCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ParentCommand.java @@ -38,6 +38,7 @@ import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; @@ -123,7 +124,7 @@ public List tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentL .at(0, CompletionSupplier.startsWith(() -> getTargets(plugin).stream())) .at(1, CompletionSupplier.startsWith(() -> getChildren().stream() .filter(s -> s.isAuthorized(sender)) - .map(s -> s.getName().toLowerCase()) + .map(s -> s.getName().toLowerCase(Locale.ROOT)) )) .from(2, partial -> getChildren().stream() .filter(s -> s.isAuthorized(sender)) @@ -137,7 +138,7 @@ public List tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentL return TabCompleter.create() .at(0, CompletionSupplier.startsWith(() -> getChildren().stream() .filter(s -> s.isAuthorized(sender)) - .map(s -> s.getName().toLowerCase()) + .map(s -> s.getName().toLowerCase(Locale.ROOT)) )) .from(1, partial -> getChildren().stream() .filter(s -> s.isAuthorized(sender)) diff --git a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/SingleCommand.java b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/SingleCommand.java index ea9663f7c..4575e5939 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/SingleCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/SingleCommand.java @@ -38,6 +38,7 @@ import net.kyori.adventure.text.format.NamedTextColor; import java.util.List; +import java.util.Locale; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -62,7 +63,7 @@ public void sendUsage(Sender sender, String label) { TextComponent.Builder builder = Component.text() .append(Component.text('>', NamedTextColor.DARK_AQUA)) .append(Component.space()) - .append(Component.text(getName().toLowerCase(), NamedTextColor.GREEN)); + .append(Component.text(getName().toLowerCase(Locale.ROOT), NamedTextColor.GREEN)); if (getArgs().isPresent()) { List argUsages = getArgs().get().stream() diff --git a/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java b/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java index ebf9f665c..fe5a97409 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Locale; /** * An enumeration of the command defintion/usage messages used in the plugin. @@ -437,7 +438,7 @@ public List args() { } public String key() { - return name().toLowerCase().replace('_', '-'); + return name().toLowerCase(Locale.ROOT).replace('_', '-'); } private static PartialArgument arg(String id, String name, boolean required) { diff --git a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompleter.java b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompleter.java index 07a9b5e4a..da985c438 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompleter.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompleter.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Predicate; @@ -112,7 +113,7 @@ static Predicate containsIgnoreCase(String substring) { if (string.length() < substring.length()) { return false; } - return string.toLowerCase().contains(substring.toLowerCase()); + return string.toLowerCase(Locale.ROOT).contains(substring.toLowerCase(Locale.ROOT)); }; } diff --git a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompletions.java b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompletions.java index f2bbdab6a..e87c7fc13 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompletions.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompletions.java @@ -36,6 +36,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; @@ -57,7 +58,7 @@ public TabCompletions(LuckPermsPlugin plugin) { this.permissions = partial -> { PermissionRegistry cache = plugin.getPermissionRegistry(); - String start = partial.toLowerCase(); + String start = partial.toLowerCase(Locale.ROOT); List parts = new ArrayList<>(Splitter.on('.').splitToList(start)); TreeNode root = cache.getRootNode(); diff --git a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentList.java b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentList.java index 016d7da04..09216ee66 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentList.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentList.java @@ -47,6 +47,7 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.UUID; import java.util.function.Predicate; @@ -101,7 +102,7 @@ public int getIntOrDefault(int index, int defaultValue) { } public String getLowercase(int index, Predicate test) throws ArgumentException.DetailedUsage { - String arg = get(index).toLowerCase(); + String arg = get(index).toLowerCase(Locale.ROOT); if (!test.test(arg)) { throw new ArgumentException.DetailedUsage(); } @@ -188,7 +189,7 @@ public Optional getTemporaryModifierAndRemove(int in } private static TemporaryNodeMergeStrategy parseTemporaryModifier(String s) { - switch (s.toLowerCase()) { + switch (s.toLowerCase(Locale.ROOT)) { case "accumulate": return TemporaryNodeMergeStrategy.ADD_NEW_DURATION_TO_EXISTING; case "replace": diff --git a/common/src/main/java/me/lucko/luckperms/common/command/utils/SortType.java b/common/src/main/java/me/lucko/luckperms/common/command/utils/SortType.java index 7a08413f6..1cc2b5913 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/utils/SortType.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/utils/SortType.java @@ -25,6 +25,8 @@ package me.lucko.luckperms.common.command.utils; +import java.util.Locale; + public enum SortType { PRIORITY, @@ -32,6 +34,6 @@ public enum SortType { @Override public String toString() { - return name().toLowerCase(); + return name().toLowerCase(Locale.ROOT); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddChatMeta.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddChatMeta.java index 66eb7e8ee..8df2295ac 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddChatMeta.java @@ -47,6 +47,7 @@ import net.luckperms.api.node.ChatMetaType; import java.util.List; +import java.util.Locale; public class MetaAddChatMeta extends GenericChildCommand { @@ -99,7 +100,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ Message.ADD_CHATMETA_SUCCESS.send(sender, target, this.type, meta, priority, context); LoggedAction.build().source(sender).target(target) - .description("meta" , "add" + this.type.name().toLowerCase(), priority, meta, context) + .description("meta" , "add" + this.type.name().toLowerCase(Locale.ROOT), priority, meta, context) .build().submit(plugin, sender); StorageAssistant.save(target, sender, plugin); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java index a2050e03f..b37c73451 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java @@ -50,6 +50,7 @@ import java.time.Duration; import java.util.List; +import java.util.Locale; public class MetaAddTempChatMeta extends GenericChildCommand { @@ -107,7 +108,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ Message.ADD_TEMP_CHATMETA_SUCCESS.send(sender, target, this.type, meta, priority, duration, context); LoggedAction.build().source(sender).target(target) - .description("meta" , "addtemp" + this.type.name().toLowerCase(), priority, meta, duration, context) + .description("meta" , "addtemp" + this.type.name().toLowerCase(Locale.ROOT), priority, meta, duration, context) .build().submit(plugin, sender); StorageAssistant.save(target, sender, plugin); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaClear.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaClear.java index 6f8a66b05..5bd4bfc80 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaClear.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaClear.java @@ -46,6 +46,7 @@ import net.luckperms.api.node.NodeType; import java.util.List; +import java.util.Locale; public class MetaClear extends GenericChildCommand { public MetaClear() { @@ -61,7 +62,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ NodeType type = null; if (!args.isEmpty()) { - String typeId = args.get(0).toLowerCase(); + String typeId = args.get(0).toLowerCase(Locale.ROOT); if (typeId.equals("any") || typeId.equals("all") || typeId.equals("*")) { type = NodeType.META_OR_CHAT_META; } @@ -104,7 +105,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ } int changed = before - target.normalData().size(); - Message.META_CLEAR_SUCCESS.send(sender, target, type.name().toLowerCase(), context, changed); + Message.META_CLEAR_SUCCESS.send(sender, target, type.name().toLowerCase(Locale.ROOT), context, changed); LoggedAction.build().source(sender).target(target) .description("meta", "clear", context) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaRemoveChatMeta.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaRemoveChatMeta.java index 6d3e2889d..9fc80c568 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaRemoveChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaRemoveChatMeta.java @@ -47,6 +47,7 @@ import net.luckperms.api.node.ChatMetaType; import java.util.List; +import java.util.Locale; public class MetaRemoveChatMeta extends GenericChildCommand { @@ -100,7 +101,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ Message.BULK_REMOVE_CHATMETA_SUCCESS.send(sender, target, this.type, priority, context); LoggedAction.build().source(sender).target(target) - .description("meta" , "remove" + this.type.name().toLowerCase(), priority, "*", context) + .description("meta" , "remove" + this.type.name().toLowerCase(Locale.ROOT), priority, "*", context) .build().submit(plugin, sender); StorageAssistant.save(target, sender, plugin); @@ -113,7 +114,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ Message.REMOVE_CHATMETA_SUCCESS.send(sender, target, this.type, meta, priority, context); LoggedAction.build().source(sender).target(target) - .description("meta" , "remove" + this.type.name().toLowerCase(), priority, meta, context) + .description("meta" , "remove" + this.type.name().toLowerCase(Locale.ROOT), priority, meta, context) .build().submit(plugin, sender); StorageAssistant.save(target, sender, plugin); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaRemoveTempChatMeta.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaRemoveTempChatMeta.java index cd8951103..19c391ec9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaRemoveTempChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaRemoveTempChatMeta.java @@ -47,6 +47,7 @@ import net.luckperms.api.node.ChatMetaType; import java.util.List; +import java.util.Locale; public class MetaRemoveTempChatMeta extends GenericChildCommand { @@ -100,7 +101,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ Message.BULK_REMOVE_TEMP_CHATMETA_SUCCESS.send(sender, target, this.type, priority, context); LoggedAction.build().source(sender).target(target) - .description("meta" , "removetemp" + this.type.name().toLowerCase(), priority, "*", context) + .description("meta" , "removetemp" + this.type.name().toLowerCase(Locale.ROOT), priority, "*", context) .build().submit(plugin, sender); StorageAssistant.save(target, sender, plugin); @@ -113,7 +114,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ Message.REMOVE_TEMP_CHATMETA_SUCCESS.send(sender, target, this.type, meta, priority, context); LoggedAction.build().source(sender).target(target) - .description("meta" , "removetemp" + this.type.name().toLowerCase(), priority, meta, context) + .description("meta" , "removetemp" + this.type.name().toLowerCase(Locale.ROOT), priority, meta, context) .build().submit(plugin, sender); StorageAssistant.save(target, sender, plugin); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetChatMeta.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetChatMeta.java index 4f115f64c..6a6da7122 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetChatMeta.java @@ -50,6 +50,7 @@ import net.luckperms.api.node.ChatMetaType; import java.util.List; +import java.util.Locale; import java.util.OptionalInt; public class MetaSetChatMeta extends GenericChildCommand { @@ -134,7 +135,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ Message.ADD_CHATMETA_SUCCESS.send(sender, target, this.type, meta, priority, context); LoggedAction.build().source(sender).target(target) - .description("meta" , "set" + this.type.name().toLowerCase(), priority, meta, context) + .description("meta" , "set" + this.type.name().toLowerCase(Locale.ROOT), priority, meta, context) .build().submit(plugin, sender); StorageAssistant.save(target, sender, plugin); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTempChatMeta.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTempChatMeta.java index 964049533..09bb41a71 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTempChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTempChatMeta.java @@ -53,6 +53,7 @@ import java.time.Duration; import java.util.List; +import java.util.Locale; import java.util.OptionalInt; public class MetaSetTempChatMeta extends GenericChildCommand { @@ -146,7 +147,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ Message.ADD_TEMP_CHATMETA_SUCCESS.send(sender, target, this.type, meta, priority, duration, context); LoggedAction.build().source(sender).target(target) - .description("meta" , "settemp" + this.type.name().toLowerCase(), priority, meta, duration, context) + .description("meta" , "settemp" + this.type.name().toLowerCase(Locale.ROOT), priority, meta, duration, context) .build().submit(plugin, sender); StorageAssistant.save(target, sender, plugin); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentClearTrack.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentClearTrack.java index 1a20d7226..7b936234d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentClearTrack.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentClearTrack.java @@ -50,6 +50,7 @@ import net.luckperms.api.node.NodeType; import java.util.List; +import java.util.Locale; public class ParentClearTrack extends GenericChildCommand { public ParentClearTrack() { @@ -63,7 +64,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ return; } - final String trackName = args.get(0).toLowerCase(); + final String trackName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.TRACK_NAME_TEST.test(trackName)) { Message.TRACK_INVALID_ENTRY.send(sender, trackName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentSetTrack.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentSetTrack.java index 36b63606b..a306da03b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentSetTrack.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentSetTrack.java @@ -50,6 +50,7 @@ import net.luckperms.api.node.NodeType; import java.util.List; +import java.util.Locale; public class ParentSetTrack extends GenericChildCommand { public ParentSetTrack() { @@ -63,7 +64,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ return; } - final String trackName = args.get(0).toLowerCase(); + final String trackName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.TRACK_NAME_TEST.test(trackName)) { Message.TRACK_INVALID_ENTRY.send(sender, trackName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/UserSwitchPrimaryGroup.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/UserSwitchPrimaryGroup.java index 483235fc7..41b2c6efb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/UserSwitchPrimaryGroup.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/UserSwitchPrimaryGroup.java @@ -51,6 +51,7 @@ import net.luckperms.api.node.NodeEqualityPredicate; import java.util.List; +import java.util.Locale; public class UserSwitchPrimaryGroup extends GenericChildCommand { public UserSwitchPrimaryGroup() { @@ -74,9 +75,9 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ Message.USER_PRIMARYGROUP_WARN_OPTION.send(sender, opt); } - Group group = plugin.getGroupManager().getIfLoaded(args.get(0).toLowerCase()); + Group group = plugin.getGroupManager().getIfLoaded(args.get(0).toLowerCase(Locale.ROOT)); if (group == null) { - Message.DOES_NOT_EXIST.send(sender, args.get(0).toLowerCase()); + Message.DOES_NOT_EXIST.send(sender, args.get(0).toLowerCase(Locale.ROOT)); return; } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/CreateGroup.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/CreateGroup.java index b7592bac8..2e9aaf5ae 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/CreateGroup.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/CreateGroup.java @@ -46,6 +46,8 @@ import net.luckperms.api.event.cause.CreationCause; import net.luckperms.api.model.data.DataType; +import java.util.Locale; + public class CreateGroup extends SingleCommand { public CreateGroup() { super(CommandSpec.CREATE_GROUP, "CreateGroup", CommandPermission.CREATE_GROUP, Predicates.notInRange(1, 3)); @@ -58,7 +60,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St return; } - String groupName = args.get(0).toLowerCase(); + String groupName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.GROUP_NAME_TEST.test(groupName)) { Message.GROUP_INVALID_ENTRY.send(sender, groupName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/DeleteGroup.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/DeleteGroup.java index 9a59556f2..57794664e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/DeleteGroup.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/DeleteGroup.java @@ -56,6 +56,7 @@ import net.luckperms.api.event.cause.DeletionCause; import java.util.List; +import java.util.Locale; import java.util.Optional; public class DeleteGroup extends SingleCommand { @@ -70,7 +71,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St return; } - String groupName = args.get(0).toLowerCase(); + String groupName = args.get(0).toLowerCase(Locale.ROOT); if (groupName.equalsIgnoreCase(GroupManager.DEFAULT_GROUP_NAME)) { Message.DELETE_GROUP_ERROR_DEFAULT.send(sender); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupClone.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupClone.java index 2461bb742..02be4069a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupClone.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupClone.java @@ -42,6 +42,8 @@ import net.luckperms.api.event.cause.CreationCause; import net.luckperms.api.model.data.DataType; +import java.util.Locale; + public class GroupClone extends ChildCommand { public GroupClone() { super(CommandSpec.GROUP_CLONE, "clone", CommandPermission.GROUP_CLONE, Predicates.not(1)); @@ -54,7 +56,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Group target, Argumen return; } - String newGroupName = args.get(0).toLowerCase(); + String newGroupName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.GROUP_NAME_TEST.test(newGroupName)) { Message.GROUP_INVALID_ENTRY.send(sender, newGroupName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupParentCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupParentCommand.java index 8262a55e0..ead05681d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupParentCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupParentCommand.java @@ -46,6 +46,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -83,7 +84,7 @@ protected String parseTarget(String target, LuckPermsPlugin plugin, Sender sende if (group != null) { return group.getName(); } else { - return target.toLowerCase(); + return target.toLowerCase(Locale.ROOT); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java index 23c625fce..0300ea2d6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupRename.java @@ -55,6 +55,7 @@ import net.luckperms.api.model.data.DataType; import java.util.List; +import java.util.Locale; import java.util.concurrent.CompletableFuture; public class GroupRename extends ChildCommand { @@ -69,7 +70,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Group target, Argumen return; } - String newGroupName = args.get(0).toLowerCase(); + String newGroupName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.GROUP_NAME_TEST.test(newGroupName)) { Message.GROUP_INVALID_ENTRY.send(sender, newGroupName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java index 6b92bb910..a5a58fa0d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java @@ -41,6 +41,7 @@ import me.lucko.luckperms.common.util.Predicates; import java.util.List; +import java.util.Locale; public class LogGroupHistory extends ChildCommand { private static final int ENTRIES_PER_PAGE = 10; @@ -51,7 +52,7 @@ public LogGroupHistory() { @Override public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) { - String group = args.get(0).toLowerCase(); + String group = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.GROUP_NAME_TEST.test(group)) { Message.GROUP_INVALID_ENTRY.send(sender, group); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java index a7a6de3f2..0a655a3c1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java @@ -41,6 +41,7 @@ import me.lucko.luckperms.common.util.Predicates; import java.util.List; +import java.util.Locale; public class LogTrackHistory extends ChildCommand { private static final int ENTRIES_PER_PAGE = 10; @@ -51,7 +52,7 @@ public LogTrackHistory() { @Override public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) { - String track = args.get(0).toLowerCase(); + String track = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.TRACK_NAME_TEST.test(track)) { Message.TRACK_INVALID_ENTRY.send(sender, track); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java index 9f6d9d78d..85feafb78 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java @@ -51,6 +51,7 @@ import me.lucko.luckperms.common.util.CaffeineFactory; import me.lucko.luckperms.common.util.Predicates; +import java.util.Locale; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -90,13 +91,13 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St bulkUpdateBuilder.trackStatistics(!args.remove("-s")); try { - bulkUpdateBuilder.dataType(DataType.valueOf(args.remove(0).toUpperCase())); + bulkUpdateBuilder.dataType(DataType.valueOf(args.remove(0).toUpperCase(Locale.ROOT))); } catch (IllegalArgumentException e) { Message.BULK_UPDATE_INVALID_DATA_TYPE.send(sender); return; } - String action = args.remove(0).toLowerCase(); + String action = args.remove(0).toLowerCase(Locale.ROOT); switch (action) { case "delete": bulkUpdateBuilder.action(DeleteAction.create()); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java index 3542ca1ad..459003b41 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java @@ -45,6 +45,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Locale; public class EditorCommand extends SingleCommand { public EditorCommand() { @@ -60,7 +61,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St String arg0 = args.getOrDefault(0, null); if (arg0 != null) { try { - type = Type.valueOf(arg0.toUpperCase()); + type = Type.valueOf(arg0.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { // assume they meant it as a filter filter = arg0; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/VerboseCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/VerboseCommand.java index a9acd5361..4f7ceb338 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/VerboseCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/VerboseCommand.java @@ -45,6 +45,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; public class VerboseCommand extends SingleCommand { public VerboseCommand() { @@ -59,7 +60,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St } VerboseHandler verboseHandler = plugin.getVerboseHandler(); - String mode = args.get(0).toLowerCase(); + String mode = args.get(0).toLowerCase(Locale.ROOT); if (mode.equals("command") || mode.equals("cmd")) { if (args.size() < 3) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/CreateTrack.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/CreateTrack.java index 7f0e76380..bb1d324a5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/CreateTrack.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/CreateTrack.java @@ -40,6 +40,8 @@ import net.luckperms.api.actionlog.Action; import net.luckperms.api.event.cause.CreationCause; +import java.util.Locale; + public class CreateTrack extends SingleCommand { public CreateTrack() { super(CommandSpec.CREATE_TRACK, "CreateTrack", CommandPermission.CREATE_TRACK, Predicates.not(1)); @@ -52,7 +54,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St return; } - String trackName = args.get(0).toLowerCase(); + String trackName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.TRACK_NAME_TEST.test(trackName)) { Message.TRACK_INVALID_ENTRY.send(sender, trackName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/DeleteTrack.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/DeleteTrack.java index 75c250a25..8dc2ad274 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/DeleteTrack.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/DeleteTrack.java @@ -44,6 +44,7 @@ import net.luckperms.api.event.cause.DeletionCause; import java.util.List; +import java.util.Locale; public class DeleteTrack extends SingleCommand { public DeleteTrack() { @@ -57,7 +58,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St return; } - String trackName = args.get(0).toLowerCase(); + String trackName = args.get(0).toLowerCase(Locale.ROOT); Track track = plugin.getStorage().loadTrack(trackName).join().orElse(null); if (track == null) { Message.TRACK_LOAD_ERROR.send(sender); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackAppend.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackAppend.java index 898dfc4b0..f0b6ae9ba 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackAppend.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackAppend.java @@ -45,6 +45,7 @@ import net.luckperms.api.model.data.DataMutateResult; import java.util.List; +import java.util.Locale; public class TrackAppend extends ChildCommand { public TrackAppend() { @@ -58,7 +59,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Track target, Argumen return; } - String groupName = args.get(0).toLowerCase(); + String groupName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.GROUP_NAME_TEST.test(groupName)) { sendDetailedUsage(sender, label); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackClone.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackClone.java index 8abddced4..c4d9e19ce 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackClone.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackClone.java @@ -42,6 +42,8 @@ import net.kyori.adventure.text.Component; import net.luckperms.api.event.cause.CreationCause; +import java.util.Locale; + public class TrackClone extends ChildCommand { public TrackClone() { super(CommandSpec.TRACK_CLONE, "clone", CommandPermission.TRACK_CLONE, Predicates.not(1)); @@ -54,7 +56,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Track target, Argumen return; } - String newTrackName = args.get(0).toLowerCase(); + String newTrackName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.TRACK_NAME_TEST.test(newTrackName)) { Message.TRACK_INVALID_ENTRY.send(sender, newTrackName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackInsert.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackInsert.java index 3c77671f4..b6fe12edc 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackInsert.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackInsert.java @@ -45,6 +45,7 @@ import net.luckperms.api.model.data.DataMutateResult; import java.util.List; +import java.util.Locale; public class TrackInsert extends ChildCommand { public TrackInsert() { @@ -58,7 +59,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Track target, Argumen return; } - String groupName = args.get(0).toLowerCase(); + String groupName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.GROUP_NAME_TEST.test(groupName)) { sendDetailedUsage(sender, label); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackParentCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackParentCommand.java index 770efc4d2..ea79419d5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackParentCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackParentCommand.java @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -68,7 +69,7 @@ public TrackParentCommand() { @Override protected String parseTarget(String target, LuckPermsPlugin plugin, Sender sender) { - return target.toLowerCase(); + return target.toLowerCase(Locale.ROOT); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackRemove.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackRemove.java index cd2464e52..01fc0c9d1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackRemove.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackRemove.java @@ -44,6 +44,7 @@ import net.luckperms.api.model.data.DataMutateResult; import java.util.List; +import java.util.Locale; public class TrackRemove extends ChildCommand { public TrackRemove() { @@ -57,7 +58,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Track target, Argumen return; } - String groupName = args.get(0).toLowerCase(); + String groupName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.GROUP_NAME_TEST.test(groupName)) { sendDetailedUsage(sender, label); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackRename.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackRename.java index c88c8a879..358d3dada 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackRename.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackRename.java @@ -43,6 +43,8 @@ import net.luckperms.api.event.cause.CreationCause; import net.luckperms.api.event.cause.DeletionCause; +import java.util.Locale; + public class TrackRename extends ChildCommand { public TrackRename() { super(CommandSpec.TRACK_RENAME, "rename", CommandPermission.TRACK_RENAME, Predicates.not(1)); @@ -55,7 +57,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Track target, Argumen return; } - String newTrackName = args.get(0).toLowerCase(); + String newTrackName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.TRACK_NAME_TEST.test(newTrackName)) { Message.TRACK_INVALID_ENTRY.send(sender, newTrackName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserDemote.java b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserDemote.java index 0dd89f5e5..d4ac566e2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserDemote.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserDemote.java @@ -47,6 +47,7 @@ import net.luckperms.api.track.DemotionResult; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.function.Predicate; @@ -77,7 +78,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, User target, Argument } } - final String trackName = args.get(0).toLowerCase(); + final String trackName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.TRACK_NAME_TEST.test(trackName)) { Message.TRACK_INVALID_ENTRY.send(sender, trackName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserPromote.java b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserPromote.java index c7faa2b11..6bf891f2f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserPromote.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserPromote.java @@ -47,6 +47,7 @@ import net.luckperms.api.track.PromotionResult; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.function.Predicate; @@ -77,7 +78,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, User target, Argument } } - final String trackName = args.get(0).toLowerCase(); + final String trackName = args.get(0).toLowerCase(Locale.ROOT); if (!DataConstraints.TRACK_NAME_TEST.test(trackName)) { Message.TRACK_INVALID_ENTRY.send(sender, trackName); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 1c4a27204..1aa8d32e7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -59,6 +59,7 @@ import java.util.EnumMap; import java.util.EnumSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -86,9 +87,9 @@ private ConfigKeys() {} * The name of the server */ public static final ConfigKey SERVER = key(c -> { - String server = c.getString("server", "global").toLowerCase(); + String server = c.getString("server", "global").toLowerCase(Locale.ROOT); if (server.equals("load-from-system-property")) { - server = System.getProperty("luckperms.server", "global").toLowerCase(); + server = System.getProperty("luckperms.server", "global").toLowerCase(Locale.ROOT); } return server; }); @@ -191,7 +192,7 @@ private ConfigKeys() {} */ public static final ConfigKey TEMPORARY_ADD_BEHAVIOUR = key(c -> { String value = c.getString("temporary-add-behaviour", "deny"); - switch (value.toLowerCase()) { + switch (value.toLowerCase(Locale.ROOT)) { case "accumulate": return TemporaryNodeMergeStrategy.ADD_NEW_DURATION_TO_EXISTING; case "replace": @@ -205,7 +206,7 @@ private ConfigKeys() {} * How primary groups should be calculated. */ public static final ConfigKey PRIMARY_GROUP_CALCULATION_METHOD = notReloadable(key(c -> { - String option = c.getString("primary-group-calculation", "stored").toLowerCase(); + String option = c.getString("primary-group-calculation", "stored").toLowerCase(Locale.ROOT); if (!option.equals("stored") && !option.equals("parents-by-weight") && !option.equals("all-parents-by-weight")) { option = "stored"; } @@ -318,7 +319,7 @@ private ConfigKeys() {} */ public static final ConfigKey INHERITANCE_TRAVERSAL_ALGORITHM = key(c -> { String value = c.getString("inheritance-traversal-algorithm", "depth-first-pre-order"); - switch (value.toLowerCase()) { + switch (value.toLowerCase(Locale.ROOT)) { case "breadth-first": return TraversalAlgorithm.BREADTH_FIRST; case "depth-first-post-order": @@ -355,7 +356,7 @@ private ConfigKeys() {} */ public static final ConfigKey> GROUP_WEIGHTS = key(c -> { return c.getStringMap("group-weight", ImmutableMap.of()).entrySet().stream().collect(ImmutableCollectors.toMap( - e -> e.getKey().toLowerCase(), + e -> e.getKey().toLowerCase(Locale.ROOT), e -> { try { return Integer.parseInt(e.getValue()); @@ -378,7 +379,7 @@ private ConfigKeys() {} String middleSpacer = l.getString("meta-formatting.prefix.middle-spacer", " "); String endSpacer = l.getString("meta-formatting.prefix.end-spacer", ""); DuplicateRemovalFunction duplicateRemovalFunction; - switch (l.getString("meta-formatting.prefix.duplicates", "").toLowerCase()) { + switch (l.getString("meta-formatting.prefix.duplicates", "").toLowerCase(Locale.ROOT)) { case "first-only": duplicateRemovalFunction = DuplicateRemovalFunction.FIRST_ONLY; break; @@ -405,7 +406,7 @@ private ConfigKeys() {} String middleSpacer = l.getString("meta-formatting.suffix.middle-spacer", " "); String endSpacer = l.getString("meta-formatting.suffix.end-spacer", ""); DuplicateRemovalFunction duplicateRemovalFunction; - switch (l.getString("meta-formatting.prefix.duplicates", "").toLowerCase()) { + switch (l.getString("meta-formatting.prefix.duplicates", "").toLowerCase(Locale.ROOT)) { case "first-only": duplicateRemovalFunction = DuplicateRemovalFunction.FIRST_ONLY; break; @@ -493,7 +494,7 @@ private ConfigKeys() {} public static final ConfigKey VAULT_SERVER = key(c -> { // default to true for backwards compatibility if (USE_VAULT_SERVER.get(c)) { - return c.getString("vault-server", "global").toLowerCase(); + return c.getString("vault-server", "global").toLowerCase(Locale.ROOT); } else { return SERVER.get(c); } @@ -520,8 +521,8 @@ private ConfigKeys() {} public static final ConfigKey WORLD_REWRITES = key(c -> { return WorldNameRewriter.of(c.getStringMap("world-rewrite", ImmutableMap.of()).entrySet().stream() .collect(ImmutableCollectors.toMap( - e -> e.getKey().toLowerCase(), - e -> e.getValue().toLowerCase() + e -> e.getKey().toLowerCase(Locale.ROOT), + e -> e.getValue().toLowerCase(Locale.ROOT) ))); }); diff --git a/common/src/main/java/me/lucko/luckperms/common/config/generic/key/ConfigKeyFactory.java b/common/src/main/java/me/lucko/luckperms/common/config/generic/key/ConfigKeyFactory.java index 58afb78b3..ac9a76773 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/generic/key/ConfigKeyFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/generic/key/ConfigKeyFactory.java @@ -29,6 +29,7 @@ import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -36,7 +37,7 @@ public interface ConfigKeyFactory { ConfigKeyFactory BOOLEAN = ConfigurationAdapter::getBoolean; ConfigKeyFactory STRING = ConfigurationAdapter::getString; - ConfigKeyFactory LOWERCASE_STRING = (adapter, path, def) -> adapter.getString(path, def).toLowerCase(); + ConfigKeyFactory LOWERCASE_STRING = (adapter, path, def) -> adapter.getString(path, def).toLowerCase(Locale.ROOT); ConfigKeyFactory> STRING_MAP = (config, path, def) -> ImmutableMap.copyOf(config.getStringMap(path, ImmutableMap.of())); static SimpleConfigKey key(Function function) { diff --git a/common/src/main/java/me/lucko/luckperms/common/context/WorldNameRewriter.java b/common/src/main/java/me/lucko/luckperms/common/context/WorldNameRewriter.java index 148ee01d4..cc52ca597 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/WorldNameRewriter.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/WorldNameRewriter.java @@ -32,6 +32,7 @@ import net.luckperms.api.context.DefaultContextKeys; import java.util.HashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -69,7 +70,7 @@ public NonEmpty(Map rewrites) { @Override public void rewriteAndSubmit(String worldName, ContextConsumer consumer) { Set seen = new HashSet<>(); - worldName = worldName.toLowerCase(); + worldName = worldName.toLowerCase(Locale.ROOT); while (Context.isValidValue(worldName) && seen.add(worldName)) { consumer.accept(DefaultContextKeys.WORLD_KEY, worldName); diff --git a/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java b/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java index 87388ce5e..b61c52e53 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java @@ -39,6 +39,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Iterator; +import java.util.Locale; import java.util.Objects; import java.util.Set; import java.util.Spliterator; @@ -127,7 +128,7 @@ static String sanitizeKey(String key) { if (!Context.isValidKey(key)) { throw new IllegalArgumentException("key is (effectively) empty"); } - return key.toLowerCase(); + return key.toLowerCase(Locale.ROOT); } static String sanitizeValue(String value) { @@ -135,7 +136,7 @@ static String sanitizeValue(String value) { if (!Context.isValidValue(value)) { throw new IllegalArgumentException("value is (effectively) empty"); } - return value.toLowerCase(); + return value.toLowerCase(Locale.ROOT); } public static boolean isGlobalServerWorldEntry(String key, String value) { diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index 9c26c5be5..9b21961c6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -35,6 +35,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.List; +import java.util.Locale; /** * The dependencies used by LuckPerms. @@ -314,7 +315,7 @@ private static String rewriteEscaping(String s) { } public String getFileName(String classifier) { - String name = name().toLowerCase().replace('_', '-'); + String name = name().toLowerCase(Locale.ROOT).replace('_', '-'); String extra = classifier == null || classifier.isEmpty() ? "" : "-" + classifier; diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java b/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java index 8fa0d4f91..db92439e9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java @@ -42,6 +42,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Locale; + public class MessagingFactory

{ private final P plugin; @@ -82,7 +84,7 @@ public final InternalMessagingService getInstance() { return null; } - this.plugin.getLogger().info("Loading messaging service... [" + messagingType.toUpperCase() + "]"); + this.plugin.getLogger().info("Loading messaging service... [" + messagingType.toUpperCase(Locale.ROOT) + "]"); InternalMessagingService service = getServiceFor(messagingType); if (service != null) { diff --git a/common/src/main/java/me/lucko/luckperms/common/metastacking/StandardStackElements.java b/common/src/main/java/me/lucko/luckperms/common/metastacking/StandardStackElements.java index 0c94af9cd..4f071abe7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/metastacking/StandardStackElements.java +++ b/common/src/main/java/me/lucko/luckperms/common/metastacking/StandardStackElements.java @@ -39,6 +39,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.List; +import java.util.Locale; import java.util.Objects; /** @@ -48,7 +49,7 @@ public final class StandardStackElements { private StandardStackElements() {} public static MetaStackElement parseFromString(LuckPermsPlugin plugin, String s) { - s = s.toLowerCase(); + s = s.toLowerCase(Locale.ROOT); // static if (s.equals("highest")) return HIGHEST; diff --git a/common/src/main/java/me/lucko/luckperms/common/model/Group.java b/common/src/main/java/me/lucko/luckperms/common/model/Group.java index af1273472..2814b8d80 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/Group.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/Group.java @@ -40,6 +40,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Locale; import java.util.Optional; import java.util.OptionalInt; @@ -68,7 +69,7 @@ public class Group extends PermissionHolder { public Group(String name, LuckPermsPlugin plugin) { super(plugin); - this.name = name.toLowerCase(); + this.name = name.toLowerCase(Locale.ROOT); this.cachedData = new GroupCachedDataManager(this); getPlugin().getEventDispatcher().dispatchGroupCacheLoad(this, this.cachedData); diff --git a/common/src/main/java/me/lucko/luckperms/common/model/HolderType.java b/common/src/main/java/me/lucko/luckperms/common/model/HolderType.java index 21bf3f1d7..a46e85f7b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/HolderType.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/HolderType.java @@ -25,12 +25,14 @@ package me.lucko.luckperms.common.model; +import java.util.Locale; + public enum HolderType { USER, GROUP; @Override public String toString() { - return name().toLowerCase(); + return name().toLowerCase(Locale.ROOT); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index 226771385..edd34ef30 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -63,6 +63,7 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.OptionalInt; import java.util.SortedSet; @@ -363,7 +364,7 @@ public > M exportPermissions(IntFunction mapFa private static void processExportedPermissions(Map accumulator, List entries, boolean convertToLowercase, boolean resolveShorthand) { for (Node node : entries) { if (convertToLowercase) { - accumulator.putIfAbsent(node.getKey().toLowerCase(), node.getValue()); + accumulator.putIfAbsent(node.getKey().toLowerCase(Locale.ROOT), node.getValue()); } else { accumulator.putIfAbsent(node.getKey(), node.getValue()); } @@ -374,7 +375,7 @@ private static void processExportedPermissions(Map accumulator, Collection shorthand = node.resolveShorthand(); for (String s : shorthand) { if (convertToLowercase) { - accumulator.putIfAbsent(s.toLowerCase(), node.getValue()); + accumulator.putIfAbsent(s.toLowerCase(Locale.ROOT), node.getValue()); } else { accumulator.putIfAbsent(s, node.getValue()); } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PrimaryGroupHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PrimaryGroupHolder.java index 1a1dc7b1a..87ceef189 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PrimaryGroupHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PrimaryGroupHolder.java @@ -32,6 +32,7 @@ import net.luckperms.api.query.QueryOptions; import java.util.LinkedHashSet; +import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -90,7 +91,7 @@ public void setStoredValue(String value) { if (value == null || value.isEmpty()) { this.value = null; } else { - this.value = value.toLowerCase(); + this.value = value.toLowerCase(Locale.ROOT); } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java b/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java index c3231afef..48613caa5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java @@ -34,6 +34,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Locale; import java.util.Map; import java.util.OptionalInt; @@ -62,7 +63,7 @@ public WeightCache(Group group) { if (!seen) { Map configWeights = this.group.getPlugin().getConfiguration().get(ConfigKeys.GROUP_WEIGHTS); - Integer value = configWeights.get(this.group.getObjectName().toLowerCase()); + Integer value = configWeights.get(this.group.getObjectName().toLowerCase(Locale.ROOT)); if (value != null) { seen = true; weight = value; diff --git a/common/src/main/java/me/lucko/luckperms/common/model/manager/group/AbstractGroupManager.java b/common/src/main/java/me/lucko/luckperms/common/model/manager/group/AbstractGroupManager.java index b600e4cf6..623599f3d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/manager/group/AbstractGroupManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/manager/group/AbstractGroupManager.java @@ -28,6 +28,7 @@ import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.manager.AbstractManager; +import java.util.Locale; import java.util.Optional; public abstract class AbstractGroupManager extends AbstractManager implements GroupManager { @@ -61,7 +62,7 @@ public T getByDisplayName(String name) { @Override protected String sanitizeIdentifier(String s) { - return s.toLowerCase(); + return s.toLowerCase(Locale.ROOT); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/model/manager/track/AbstractTrackManager.java b/common/src/main/java/me/lucko/luckperms/common/model/manager/track/AbstractTrackManager.java index f6cff287f..22469e90e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/manager/track/AbstractTrackManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/manager/track/AbstractTrackManager.java @@ -28,10 +28,12 @@ import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.manager.AbstractManager; +import java.util.Locale; + public abstract class AbstractTrackManager extends AbstractManager implements TrackManager { @Override protected String sanitizeIdentifier(String s) { - return s.toLowerCase(); + return s.toLowerCase(Locale.ROOT); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/types/DisplayName.java b/common/src/main/java/me/lucko/luckperms/common/node/types/DisplayName.java index 221bd0efd..d09b026f6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/types/DisplayName.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/types/DisplayName.java @@ -35,6 +35,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -72,7 +73,7 @@ public DisplayName(String displayName, boolean value, long expireAt, ImmutableCo } public static @Nullable Builder parse(String key) { - if (!key.toLowerCase().startsWith(NODE_MARKER)) { + if (!key.toLowerCase(Locale.ROOT).startsWith(NODE_MARKER)) { return null; } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/types/Inheritance.java b/common/src/main/java/me/lucko/luckperms/common/node/types/Inheritance.java index ab8c7191b..0fbf36087 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/types/Inheritance.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/types/Inheritance.java @@ -36,6 +36,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -44,7 +45,7 @@ public class Inheritance extends AbstractNode, Object> metadata) { super(key(groupName), value, expireAt, contexts, metadata); - this.groupName = groupName.toLowerCase(); + this.groupName = groupName.toLowerCase(Locale.ROOT); } @Override @@ -73,7 +74,7 @@ public Inheritance(String groupName, boolean value, long expireAt, ImmutableCont } public static @Nullable Builder parse(String key) { - key = key.toLowerCase(); + key = key.toLowerCase(Locale.ROOT); if (!key.startsWith(NODE_MARKER)) { return null; } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/types/Meta.java b/common/src/main/java/me/lucko/luckperms/common/node/types/Meta.java index 243285282..6dfaea7f3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/types/Meta.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/types/Meta.java @@ -37,6 +37,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Iterator; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -45,7 +46,7 @@ public class Meta extends AbstractNode implements Me private static final String NODE_MARKER = NODE_KEY + "."; public static String key(String key, String value) { - return NODE_MARKER + Delimiters.escapeCharacters(key).toLowerCase() + AbstractNode.NODE_SEPARATOR + Delimiters.escapeCharacters(value); + return NODE_MARKER + Delimiters.escapeCharacters(key).toLowerCase(Locale.ROOT) + AbstractNode.NODE_SEPARATOR + Delimiters.escapeCharacters(value); } public static Builder builder() { @@ -61,7 +62,7 @@ public static Builder builder(String key, String value) { public Meta(String metaKey, String metaValue, boolean value, long expireAt, ImmutableContextSet contexts, Map, Object> metadata) { super(key(metaKey, metaValue), value, expireAt, contexts, metadata); - this.metaKey = metaKey.toLowerCase(); + this.metaKey = metaKey.toLowerCase(Locale.ROOT); this.metaValue = metaValue; } @@ -81,7 +82,7 @@ public Meta(String metaKey, String metaValue, boolean value, long expireAt, Immu } public static @Nullable Builder parse(String key) { - if (!key.toLowerCase().startsWith(NODE_MARKER)) { + if (!key.toLowerCase(Locale.ROOT).startsWith(NODE_MARKER)) { return null; } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/types/Prefix.java b/common/src/main/java/me/lucko/luckperms/common/node/types/Prefix.java index 1408466ff..d03905bd0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/types/Prefix.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/types/Prefix.java @@ -38,6 +38,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Iterator; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -87,7 +88,7 @@ public int getPriority() { } public static @Nullable Builder parse(String key) { - if (!key.toLowerCase().startsWith(NODE_MARKER)) { + if (!key.toLowerCase(Locale.ROOT).startsWith(NODE_MARKER)) { return null; } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/types/Suffix.java b/common/src/main/java/me/lucko/luckperms/common/node/types/Suffix.java index dd78fcd0e..b7774e8e9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/types/Suffix.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/types/Suffix.java @@ -38,6 +38,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Iterator; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -87,7 +88,7 @@ public int getPriority() { } public static @Nullable Builder parse(String key) { - if (!key.toLowerCase().startsWith(NODE_MARKER)) { + if (!key.toLowerCase(Locale.ROOT).startsWith(NODE_MARKER)) { return null; } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/types/Weight.java b/common/src/main/java/me/lucko/luckperms/common/node/types/Weight.java index 13773d56c..1b4bfb2d0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/types/Weight.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/types/Weight.java @@ -35,6 +35,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.Locale; import java.util.Map; public class Weight extends AbstractNode implements WeightNode { @@ -71,7 +72,7 @@ public int getWeight() { } public static @Nullable Builder parse(String key) { - if (!key.toLowerCase().startsWith(NODE_MARKER)) { + if (!key.toLowerCase(Locale.ROOT).startsWith(NODE_MARKER)) { return null; } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java b/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java index cd98e6b2e..a75afada9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java @@ -38,6 +38,7 @@ import java.time.Instant; import java.util.Collection; import java.util.HashSet; +import java.util.Locale; import java.util.Set; public class NodeJsonSerializer { @@ -51,7 +52,7 @@ public static JsonArray serializeNodes(Collection nodes) { for (Node node : nodes) { JsonObject attributes = new JsonObject(); - attributes.addProperty("type", node.getType().name().toLowerCase()); + attributes.addProperty("type", node.getType().name().toLowerCase(Locale.ROOT)); attributes.addProperty("key", node.getKey()); attributes.addProperty("value", node.getValue()); diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 2ff3dc68d..aa1eedca3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -71,6 +71,7 @@ import java.time.LocalDate; import java.time.Month; import java.util.EnumSet; +import java.util.Locale; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -336,7 +337,7 @@ public void setMessagingService(InternalMessagingService messagingService) { @Override public Optional lookupUniqueId(String username) { // get a result from the DB cache - UUID uniqueId = getStorage().getPlayerUniqueId(username.toLowerCase()).join(); + UUID uniqueId = getStorage().getPlayerUniqueId(username.toLowerCase(Locale.ROOT)).join(); // fire the event uniqueId = getEventDispatcher().dispatchUniqueIdLookup(username, uniqueId); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/Storage.java b/common/src/main/java/me/lucko/luckperms/common/storage/Storage.java index 9cfacbb89..ee4d05c74 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/Storage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/Storage.java @@ -49,6 +49,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -171,7 +172,7 @@ public CompletableFuture>> searchUserNo public CompletableFuture createAndLoadGroup(String name, CreationCause cause) { return future(() -> { - Group group = this.implementation.createAndLoadGroup(name.toLowerCase()); + Group group = this.implementation.createAndLoadGroup(name.toLowerCase(Locale.ROOT)); if (group != null) { this.plugin.getEventDispatcher().dispatchGroupCreate(group, cause); } @@ -181,7 +182,7 @@ public CompletableFuture createAndLoadGroup(String name, CreationCause ca public CompletableFuture> loadGroup(String name) { return future(() -> { - Optional group = this.implementation.loadGroup(name.toLowerCase()); + Optional group = this.implementation.loadGroup(name.toLowerCase(Locale.ROOT)); if (group.isPresent()) { this.plugin.getEventDispatcher().dispatchGroupLoad(group.get()); } @@ -217,7 +218,7 @@ public CompletableFuture>> searchGrou public CompletableFuture createAndLoadTrack(String name, CreationCause cause) { return future(() -> { - Track track = this.implementation.createAndLoadTrack(name.toLowerCase()); + Track track = this.implementation.createAndLoadTrack(name.toLowerCase(Locale.ROOT)); if (track != null) { this.plugin.getEventDispatcher().dispatchTrackCreate(track, cause); } @@ -227,7 +228,7 @@ public CompletableFuture createAndLoadTrack(String name, CreationCause ca public CompletableFuture> loadTrack(String name) { return future(() -> { - Optional track = this.implementation.loadTrack(name.toLowerCase()); + Optional track = this.implementation.loadTrack(name.toLowerCase(Locale.ROOT)); if (track.isPresent()) { this.plugin.getEventDispatcher().dispatchTrackLoad(track.get()); } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileUuidCache.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileUuidCache.java index e05734753..c6d8497a9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileUuidCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileUuidCache.java @@ -46,6 +46,7 @@ import java.nio.file.Path; import java.util.HashSet; import java.util.Iterator; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -200,11 +201,11 @@ public String put(@NonNull UUID key, @NonNull String value) { // existing might be null if (!value.equalsIgnoreCase(existing)) { if (existing != null) { - this.reverse.remove(existing.toLowerCase(), key); + this.reverse.remove(existing.toLowerCase(Locale.ROOT), key); } } - this.reverse.put(value.toLowerCase(), key); + this.reverse.put(value.toLowerCase(Locale.ROOT), key); return existing; } @@ -213,7 +214,7 @@ public String remove(@NonNull Object k) { UUID key = (UUID) k; String username = super.remove(key); if (username != null) { - this.reverse.remove(username.toLowerCase(), key); + this.reverse.remove(username.toLowerCase(Locale.ROOT), key); } return username; } @@ -223,7 +224,7 @@ public String lookupUsername(UUID uuid) { } public Set lookupUuid(String name) { - return this.reverse.get(name.toLowerCase()); + return this.reverse.get(name.toLowerCase(Locale.ROOT)); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java index 05ac6a757..79e9bb0eb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java @@ -75,6 +75,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -521,7 +522,7 @@ public void deleteTrack(Track track) { @Override public PlayerSaveResult savePlayerData(UUID uniqueId, String username) { - username = username.toLowerCase(); + username = username.toLowerCase(Locale.ROOT); MongoCollection c = this.database.getCollection(this.prefix + "uuid"); // find any existing mapping @@ -560,7 +561,7 @@ public void deletePlayerData(UUID uniqueId) { @Override public UUID getPlayerUniqueId(String username) { MongoCollection c = this.database.getCollection(this.prefix + "uuid"); - Document doc = c.find(new Document("name", username.toLowerCase())).first(); + Document doc = c.find(new Document("name", username.toLowerCase(Locale.ROOT))).first(); if (doc != null) { return getDocumentId(doc); } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/split/SplitStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/split/SplitStorage.java index 06aa16294..81c16779d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/split/SplitStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/split/SplitStorage.java @@ -46,6 +46,7 @@ import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -114,7 +115,7 @@ public Map getMeta() { meta.put( Component.translatable("luckperms.command.info.storage.meta.split-types-key"), Message.formatStringList(this.types.entrySet().stream() - .map(e -> e.getKey().toString().toLowerCase() + "->" + e.getValue().getName().toLowerCase()) + .map(e -> e.getKey().toString().toLowerCase(Locale.ROOT) + "->" + e.getValue().getName().toLowerCase(Locale.ROOT)) .collect(Collectors.toList())) ); for (StorageImplementation backing : this.implementations.values()) { diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index e9bd93bbb..d1a4e60db 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -72,6 +72,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -173,7 +174,7 @@ public void init() throws Exception { private void applySchema() throws IOException, SQLException { List statements; - String schemaFileName = "me/lucko/luckperms/schema/" + this.connectionFactory.getImplementationName().toLowerCase() + ".sql"; + String schemaFileName = "me/lucko/luckperms/schema/" + this.connectionFactory.getImplementationName().toLowerCase(Locale.ROOT) + ".sql"; try (InputStream is = this.plugin.getBootstrap().getResourceStream(schemaFileName)) { if (is == null) { throw new IOException("Couldn't locate schema file for " + this.connectionFactory.getImplementationName()); @@ -375,7 +376,7 @@ public void saveUser(User user) throws SQLException { updateUserPermissions(c, user.getUniqueId(), changes.getAdded(), changes.getRemoved()); insertPlayerData(c, user.getUniqueId(), new SqlPlayerData( user.getPrimaryGroup().getStoredValue().orElse(GroupManager.DEFAULT_GROUP_NAME), - user.getUsername().orElse("null").toLowerCase() + user.getUsername().orElse("null").toLowerCase(Locale.ROOT) )); } } @@ -605,7 +606,7 @@ public void deleteTrack(Track track) throws SQLException { @Override public PlayerSaveResult savePlayerData(UUID uniqueId, String username) throws SQLException { - username = username.toLowerCase(); + username = username.toLowerCase(Locale.ROOT); // find any existing mapping String oldUsername = getPlayerName(uniqueId); @@ -672,7 +673,7 @@ public void deletePlayerData(UUID uniqueId) throws SQLException { @Override public UUID getPlayerUniqueId(String username) throws SQLException { - username = username.toLowerCase(); + username = username.toLowerCase(Locale.ROOT); try (Connection c = this.connectionFactory.getConnection()) { try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_SELECT_UUID_BY_USERNAME))) { ps.setString(1, username); @@ -907,7 +908,7 @@ private Set selectGroups(Connection c) throws SQLException { try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(GROUP_SELECT_ALL))) { try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { - groups.add(rs.getString("name").toLowerCase()); + groups.add(rs.getString("name").toLowerCase(Locale.ROOT)); } } } @@ -992,7 +993,7 @@ private Set selectTracks(Connection c) throws SQLException { try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(TRACK_SELECT_ALL))) { try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { - tracks.add(rs.getString("name").toLowerCase()); + tracks.add(rs.getString("name").toLowerCase(Locale.ROOT)); } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionRegistry.java b/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionRegistry.java index e02c14448..9d2aba13e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionRegistry.java +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionRegistry.java @@ -32,6 +32,7 @@ import me.lucko.luckperms.common.util.ImmutableCollectors; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; @@ -93,7 +94,7 @@ public void insert(String permission) { } private void doInsert(String permission) { - permission = permission.toLowerCase(); + permission = permission.toLowerCase(Locale.ROOT); // split the permission up into parts Iterable parts = DOT_SPLIT.split(permission); diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java index a015e5920..ee2feaa69 100644 --- a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java @@ -46,6 +46,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.zip.GZIPOutputStream; @@ -160,7 +161,7 @@ public String uploadPasteData(BytebinClient bytebin, Sender sender, User user, P checks = new JObject(); for (Map.Entry node : this.view.getNodeEndings()) { String permission = prefix + node.getValue(); - checks.add(permission, checker.checkPermission(permission, PermissionCheckEvent.Origin.INTERNAL).result().name().toLowerCase()); + checks.add(permission, checker.checkPermission(permission, PermissionCheckEvent.Origin.INTERNAL).result().name().toLowerCase(Locale.ROOT)); } } else { checks = null; diff --git a/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java b/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java index 0e90e881e..c5d624cdf 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java @@ -34,6 +34,7 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.Locale; /** * Formats durations to a readable form @@ -115,7 +116,7 @@ public Component format(Duration duration) { private TranslatableComponent formatPart(long amount, ChronoUnit unit) { String format = this.concise ? "short" : amount == 1 ? "singular" : "plural"; - String translationKey = "luckperms.duration.unit." + unit.name().toLowerCase() + "." + format; + String translationKey = "luckperms.duration.unit." + unit.name().toLowerCase(Locale.ROOT) + "." + format; return Component.translatable(translationKey, Component.text(amount)); } diff --git a/common/src/main/java/me/lucko/luckperms/common/util/EnumNamer.java b/common/src/main/java/me/lucko/luckperms/common/util/EnumNamer.java index 586b62326..13465f6c2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/EnumNamer.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/EnumNamer.java @@ -26,6 +26,7 @@ package me.lucko.luckperms.common.util; import java.util.Collections; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -35,7 +36,7 @@ * @param the enum type */ public class EnumNamer> { - public static final Function, String> LOWER_CASE_NAME = value -> value.name().toLowerCase(); + public static final Function, String> LOWER_CASE_NAME = value -> value.name().toLowerCase(Locale.ROOT); private final String[] names; private final Function namingFunction; diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/event/MetaCheckEvent.java b/common/src/main/java/me/lucko/luckperms/common/verbose/event/MetaCheckEvent.java index 6c74b70ae..bcfa2114a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/event/MetaCheckEvent.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/event/MetaCheckEvent.java @@ -30,6 +30,8 @@ import net.luckperms.api.query.QueryOptions; +import java.util.Locale; + public class MetaCheckEvent extends VerboseEvent { /** @@ -71,14 +73,14 @@ protected void serializeTo(JObject object) { object.add("type", "meta") .add("key", this.key) .add("result", this.result) - .add("origin", this.origin.name().toLowerCase()); + .add("origin", this.origin.name().toLowerCase(Locale.ROOT)); } @Override public boolean eval(String variable) { return variable.equals("meta") || getCheckTarget().describe().equalsIgnoreCase(variable) || - getKey().toLowerCase().startsWith(variable.toLowerCase()) || + getKey().toLowerCase(Locale.ROOT).startsWith(variable.toLowerCase(Locale.ROOT)) || getResult().equalsIgnoreCase(variable); } diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/event/PermissionCheckEvent.java b/common/src/main/java/me/lucko/luckperms/common/verbose/event/PermissionCheckEvent.java index 48b200ab9..a646aa079 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/event/PermissionCheckEvent.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/event/PermissionCheckEvent.java @@ -31,6 +31,8 @@ import net.luckperms.api.query.QueryOptions; +import java.util.Locale; + public class PermissionCheckEvent extends VerboseEvent { /** @@ -72,7 +74,7 @@ protected void serializeTo(JObject object) { object.add("type", "permission"); object.add("permission", this.permission); - object.add("result", this.result.result().name().toLowerCase()); + object.add("result", this.result.result().name().toLowerCase(Locale.ROOT)); if (this.result.processorClass() != null || this.result.cause() != null) { JObject resultInfo = new JObject(); if (this.result.processorClass() != null) { @@ -84,14 +86,14 @@ protected void serializeTo(JObject object) { object.add("resultInfo", resultInfo); } - object.add("origin", this.origin.name().toLowerCase()); + object.add("origin", this.origin.name().toLowerCase(Locale.ROOT)); } @Override public boolean eval(String variable) { return variable.equals("permission") || getCheckTarget().describe().equalsIgnoreCase(variable) || - getPermission().toLowerCase().startsWith(variable.toLowerCase()) || + getPermission().toLowerCase(Locale.ROOT).startsWith(variable.toLowerCase(Locale.ROOT)) || getResult().result().name().equalsIgnoreCase(variable); } diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java b/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java index aff8d66a8..577d1fab0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java @@ -37,6 +37,7 @@ import net.luckperms.api.query.QueryMode; import net.luckperms.api.query.QueryOptions; +import java.util.Locale; import java.util.Objects; import java.util.UUID; @@ -113,7 +114,7 @@ public JsonObject toJson(StackTracePrinter tracePrinter) { } }) ) - .add("queryMode", this.checkQueryOptions.mode().name().toLowerCase()) + .add("queryMode", this.checkQueryOptions.mode().name().toLowerCase(Locale.ROOT)) .consume(obj -> { if (this.checkQueryOptions.mode() == QueryMode.CONTEXTUAL) { obj.add("context", new JArray() diff --git a/common/src/test/java/me/lucko/luckperms/common/util/EnumNamerTest.java b/common/src/test/java/me/lucko/luckperms/common/util/EnumNamerTest.java index e3c517ce5..d2d36e327 100644 --- a/common/src/test/java/me/lucko/luckperms/common/util/EnumNamerTest.java +++ b/common/src/test/java/me/lucko/luckperms/common/util/EnumNamerTest.java @@ -29,6 +29,8 @@ import org.junit.jupiter.api.Test; +import java.util.Locale; + import static org.junit.jupiter.api.Assertions.assertEquals; public class EnumNamerTest { @@ -38,7 +40,7 @@ void testSimple() { EnumNamer namer = new EnumNamer<>( TestEnum.class, ImmutableMap.of(TestEnum.THING, "hi"), - v -> v.name().toLowerCase().replace('_', '-') + v -> v.name().toLowerCase(Locale.ROOT).replace('_', '-') ); assertEquals("test", namer.name(TestEnum.TEST)); diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitPlayerCalculator.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitPlayerCalculator.java index c55e65626..a31fab60b 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitPlayerCalculator.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitPlayerCalculator.java @@ -47,6 +47,7 @@ import cn.nukkit.event.player.PlayerGameModeChangeEvent; import cn.nukkit.level.Level; +import java.util.Locale; import java.util.Set; public class NukkitPlayerCalculator implements ContextCalculator, Listener { @@ -111,7 +112,7 @@ private static String getGamemodeName(int mode) { case Player.CREATIVE: return "creative"; case Player.ADVENTURE: return "adventure"; case Player.SPECTATOR: return "spectator"; - default: return Server.getGamemodeString(mode, true).toLowerCase(); + default: return Server.getGamemodeString(mode, true).toLowerCase(Locale.ROOT); } } diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/PermissionDefault.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/PermissionDefault.java index da0a42d49..b1180d658 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/PermissionDefault.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/PermissionDefault.java @@ -30,6 +30,7 @@ import cn.nukkit.permission.Permission; import java.util.HashMap; +import java.util.Locale; import java.util.Map; /** @@ -84,7 +85,7 @@ public boolean getValue(boolean op) { * @return Specified value, or null if not found */ public static @Nullable PermissionDefault getByName(String name) { - return LOOKUP.get(name.toLowerCase().replaceAll("[^a-z!]", "")); + return LOOKUP.get(name.toLowerCase(Locale.ROOT).replaceAll("[^a-z!]", "")); } public static @Nullable PermissionDefault fromPermission(@Nullable Permission permission) { diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissionAttachment.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissionAttachment.java index 36e44ed42..811edef3f 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissionAttachment.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissionAttachment.java @@ -47,6 +47,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -238,7 +239,7 @@ public void setPermission(String name, boolean value) { Objects.requireNonNull(name, "name is null"); Preconditions.checkArgument(!name.isEmpty(), "name is empty"); - String permission = name.toLowerCase(); + String permission = name.toLowerCase(Locale.ROOT); Boolean previous = this.perms.put(permission, value); if (previous != null && previous == value) { @@ -275,7 +276,7 @@ public void unsetPermission(String name, boolean value) { Objects.requireNonNull(name, "name is null"); Preconditions.checkArgument(!name.isEmpty(), "name is empty"); - String permission = name.toLowerCase(); + String permission = name.toLowerCase(Locale.ROOT); Boolean previous = this.perms.remove(permission); if (previous == null) { diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/server/LuckPermsDefaultsMap.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/server/LuckPermsDefaultsMap.java index 5ca5af244..a632cb8b3 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/server/LuckPermsDefaultsMap.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/server/LuckPermsDefaultsMap.java @@ -40,6 +40,7 @@ import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -159,7 +160,7 @@ private final class DefaultsCache extends Cache> { protected @NonNull Map supply() { Map builder = new HashMap<>(); for (Permission perm : LuckPermsDefaultsMap.this.get(this.op).values()) { - String name = perm.getName().toLowerCase(); + String name = perm.getName().toLowerCase(Locale.ROOT); builder.put(name, true); for (Map.Entry child : LuckPermsDefaultsMap.this.plugin.getPermissionMap().getChildPermissions(name, true).entrySet()) { builder.putIfAbsent(child.getKey(), child.getValue()); diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/server/LuckPermsPermissionMap.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/server/LuckPermsPermissionMap.java index 316158a3b..10ca83e72 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/server/LuckPermsPermissionMap.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/server/LuckPermsPermissionMap.java @@ -41,6 +41,7 @@ import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -180,7 +181,7 @@ private void resolveChildren(Map accumulator, Map loadCollection(@NonNull String s) { @Override public @NonNull Optional getCollection(String s) { - return Optional.ofNullable(this.handle.getLoadedCollections().get(s.toLowerCase())).map(LPSubjectCollection::sponge); + return Optional.ofNullable(this.handle.getLoadedCollections().get(s.toLowerCase(Locale.ROOT))).map(LPSubjectCollection::sponge); } @Override public CompletableFuture hasCollection(String s) { - return CompletableFuture.completedFuture(this.handle.getLoadedCollections().containsKey(s.toLowerCase())); + return CompletableFuture.completedFuture(this.handle.getLoadedCollections().containsKey(s.toLowerCase(Locale.ROOT))); } @Override @@ -113,7 +114,7 @@ public CompletableFuture> getAllIdentifiers() { Objects.requireNonNull(subjectIdentifier, "subjectIdentifier"); // test the identifiers - String collection = collectionIdentifier.toLowerCase(); + String collection = collectionIdentifier.toLowerCase(Locale.ROOT); if (collection.equals("user") && !this.handle.getUserSubjects().getIdentifierValidityPredicate().test(subjectIdentifier)) { throw new IllegalArgumentException("Subject identifier '" + subjectIdentifier + "' does not pass the validity predicate for the user subject collection"); } else if (collection.equals("group") && !this.handle.getGroupSubjects().getIdentifierValidityPredicate().test(subjectIdentifier)) { diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java index a57d32c29..87566a65c 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java @@ -38,6 +38,7 @@ import org.spongepowered.api.service.permission.SubjectReference; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -116,8 +117,8 @@ private static final class SubjectReferenceAttributes { private final int hashCode; private SubjectReferenceAttributes(String collectionId, String id) { - this.collectionId = collectionId.toLowerCase(); - this.id = id.toLowerCase(); + this.collectionId = collectionId.toLowerCase(Locale.ROOT); + this.id = id.toLowerCase(Locale.ROOT); this.hashCode = calculateHashCode(); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionSet.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionSet.java index 2a559ac5d..9dc9e00a3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionSet.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/PermissionSet.java @@ -38,6 +38,8 @@ import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.util.Tristate; +import java.util.Locale; + public class PermissionSet extends ChildCommand { public PermissionSet() { super(CommandSpec.SPONGE_PERMISSION_SET, "set", CommandPermission.SPONGE_PERMISSION_SET, Predicates.inRange(0, 1)); @@ -50,7 +52,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subject ImmutableContextSet contextSet = args.getContextOrEmpty(2); if (subjectData.setPermission(contextSet, node, tristate).join()) { - SpongeCommandUtils.sendPrefixed(sender, "&aSet &b" + node + "&a to &b" + tristate.toString().toLowerCase() + "&a in context " + SpongeCommandUtils.contextToString(contextSet)); + SpongeCommandUtils.sendPrefixed(sender, "&aSet &b" + node + "&a to &b" + tristate.toString().toLowerCase(Locale.ROOT) + "&a in context " + SpongeCommandUtils.contextToString(contextSet)); } else { SpongeCommandUtils.sendPrefixed(sender, "Unable to set permission. Does the Subject already have it set?"); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeCommandUtils.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeCommandUtils.java index 501829e7b..e976b518d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeCommandUtils.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeCommandUtils.java @@ -36,6 +36,7 @@ import net.luckperms.api.util.Tristate; import java.util.Collection; +import java.util.Locale; import java.util.Map; public final class SpongeCommandUtils { @@ -46,7 +47,7 @@ public static void sendPrefixed(Sender sender, String message) { } public static Tristate parseTristate(int index, ArgumentList args) throws ArgumentException { - String s = args.get(index).toLowerCase(); + String s = args.get(index).toLowerCase(Locale.ROOT); switch (s) { case "1": case "true": diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeParentCommand.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeParentCommand.java index fd9eff857..3eaf93df1 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeParentCommand.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeParentCommand.java @@ -45,6 +45,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; @@ -131,12 +132,12 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Void v, ArgumentList } boolean persistent = true; - if (args.get(2).toLowerCase().startsWith("-t")) { + if (args.get(2).toLowerCase(Locale.ROOT).startsWith("-t")) { persistent = false; args.remove(2); } - String type = args.get(2).toLowerCase(); + String type = args.get(2).toLowerCase(Locale.ROOT); if (!type.equals("permission") && !type.equals("parent") && !type.equals("option")) { sendDetailedUsage(sender, label); return; @@ -187,7 +188,7 @@ public void sendUsage(Sender sender, String label) { public void sendDetailedUsage(Sender sender, String label) { SpongeCommandUtils.sendPrefixed(sender, "&b" + getName() + " Sub Commands: &7(" + String.format("/%s sponge [-transient]", label) + " ...)"); for (String s : Arrays.asList("Permission", "Parent", "Option")) { - List> subs = this.children.get(s.toLowerCase()).stream() + List> subs = this.children.get(s.toLowerCase(Locale.ROOT)).stream() .filter(sub -> sub.isAuthorized(sender)) .collect(Collectors.toList()); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongePlatformListener.java b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongePlatformListener.java index 6aacad42c..0ca6d58e5 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongePlatformListener.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongePlatformListener.java @@ -32,6 +32,8 @@ import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.command.SendCommandEvent; +import java.util.Locale; + public class SpongePlatformListener { private final LPSpongePlugin plugin; @@ -44,7 +46,7 @@ public void onSendCommand(SendCommandEvent e) { CommandSource source = e.getCause().first(CommandSource.class).orElse(null); if (source == null) return; - final String name = e.getCommand().toLowerCase(); + final String name = e.getCommand().toLowerCase(Locale.ROOT); if ((name.equals("op") || name.equals("minecraft:op")) && source.hasPermission("minecraft.command.op") || (name.equals("deop") || name.equals("minecraft:deop")) && source.hasPermission("minecraft.command.deop")) { Message.OP_DISABLED_SPONGE.send(this.plugin.getSenderFactory().wrap(source)); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java index 161ee9ef5..aababb710 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java @@ -55,6 +55,7 @@ import org.spongepowered.api.service.permission.SubjectCollection; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -132,12 +133,12 @@ public CompletableFuture loadSubject(String identifier) { throw new IllegalArgumentException("Illegal subject identifier"); } - LPSubject present = this.subjectLoadingCache.getIfPresent(identifier.toLowerCase()); + LPSubject present = this.subjectLoadingCache.getIfPresent(identifier.toLowerCase(Locale.ROOT)); if (present != null) { return CompletableFuture.completedFuture(present); } - return CompletableFuture.supplyAsync(() -> this.subjectLoadingCache.get(identifier.toLowerCase()), this.plugin.getBootstrap().getScheduler().async()); + return CompletableFuture.supplyAsync(() -> this.subjectLoadingCache.get(identifier.toLowerCase(Locale.ROOT)), this.plugin.getBootstrap().getScheduler().async()); } @Override @@ -146,7 +147,7 @@ public Optional getSubject(String identifier) { return Optional.empty(); } - return Optional.ofNullable(getIfLoaded(identifier.toLowerCase())).map(SpongeGroup::sponge); + return Optional.ofNullable(getIfLoaded(identifier.toLowerCase(Locale.ROOT))).map(SpongeGroup::sponge); } @Override @@ -155,7 +156,7 @@ public CompletableFuture hasRegistered(String identifier) { return CompletableFuture.completedFuture(false); } - return CompletableFuture.completedFuture(isLoaded(identifier.toLowerCase())); + return CompletableFuture.completedFuture(isLoaded(identifier.toLowerCase(Locale.ROOT))); } @Override @@ -166,7 +167,7 @@ public CompletableFuture> loadSubjects(Set getIdentifierValidityPredicate() { @Override public LPSubjectCollection getCollection(String s) { Objects.requireNonNull(s); - return this.collections.get(s.toLowerCase()); + return this.collections.get(s.toLowerCase(Locale.ROOT)); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java index f98843789..8c1ab6dfb 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java @@ -48,6 +48,7 @@ import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -160,10 +161,10 @@ public CompletableFuture setPermission(ImmutableContextSet contexts, St boolean b; if (value == Tristate.UNDEFINED) { Map perms = this.permissions.get(contexts); - b = perms != null && perms.remove(permission.toLowerCase()) != null; + b = perms != null && perms.remove(permission.toLowerCase(Locale.ROOT)) != null; } else { Map perms = this.permissions.computeIfAbsent(contexts, c -> new ConcurrentHashMap<>()); - b = !Objects.equals(perms.put(permission.toLowerCase(), value.asBoolean()), value.asBoolean()); + b = !Objects.equals(perms.put(permission.toLowerCase(Locale.ROOT), value.asBoolean()), value.asBoolean()); } if (b) { this.service.invalidateAllCaches(); @@ -312,7 +313,7 @@ public Map resolveOptions(QueryOptions filter) { @Override public CompletableFuture setOption(ImmutableContextSet contexts, String key, String value) { Map options = this.options.computeIfAbsent(contexts, c -> new ConcurrentHashMap<>()); - boolean b = !stringEquals(options.put(key.toLowerCase(), value), value); + boolean b = !stringEquals(options.put(key.toLowerCase(Locale.ROOT), value), value); if (b) { this.service.invalidateAllCaches(); } @@ -322,7 +323,7 @@ public CompletableFuture setOption(ImmutableContextSet contexts, String @Override public CompletableFuture unsetOption(ImmutableContextSet contexts, String key) { Map options = this.options.get(contexts); - boolean b = options != null && options.remove(key.toLowerCase()) != null; + boolean b = options != null && options.remove(key.toLowerCase(Locale.ROOT)) != null; if (b) { this.service.invalidateAllCaches(); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubjectData.java index b510ef1d4..39f07e578 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubjectData.java @@ -61,6 +61,7 @@ import java.util.Collection; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -297,7 +298,7 @@ public CompletableFuture setOption(ImmutableContextSet contexts, String Node node; if (key.equalsIgnoreCase(Prefix.NODE_KEY) || key.equalsIgnoreCase(Suffix.NODE_KEY)) { // special handling. - ChatMetaType type = ChatMetaType.valueOf(key.toUpperCase()); + ChatMetaType type = ChatMetaType.valueOf(key.toUpperCase(Locale.ROOT)); // remove all prefixes/suffixes from the user this.holder.removeIf(this.type, contexts, type.nodeType()::matches, false); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java index c5e7a0df3..9aad7faf1 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java @@ -45,6 +45,7 @@ import org.spongepowered.api.service.permission.SubjectCollection; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -88,7 +89,7 @@ public PersistedCollection(LuckPermsService service, String identifier) { public void loadAll() { Map holders = this.service.getStorage().loadAllFromFile(this.identifier); for (Map.Entry e : holders.entrySet()) { - PersistedSubject subject = this.subjects.get(e.getKey().toLowerCase()); + PersistedSubject subject = this.subjects.get(e.getKey().toLowerCase(Locale.ROOT)); if (subject != null) { subject.loadData(e.getValue()); } @@ -121,7 +122,7 @@ public boolean isDefaultsCollection() { } public LPSubject obtainSubject(String identifier) { - return this.subjects.get(identifier.toLowerCase()); + return this.subjects.get(identifier.toLowerCase(Locale.ROOT)); } @Override @@ -138,14 +139,14 @@ public Optional getSubject(String identifier) { @Override public CompletableFuture hasRegistered(String identifier) { - return CompletableFuture.completedFuture(this.subjects.containsKey(identifier.toLowerCase())); + return CompletableFuture.completedFuture(this.subjects.containsKey(identifier.toLowerCase(Locale.ROOT))); } @Override public CompletableFuture> loadSubjects(Set identifiers) { ImmutableSet.Builder subjects = ImmutableSet.builder(); for (String id : identifiers) { - subjects.add(Objects.requireNonNull(this.subjects.get(id.toLowerCase()))); + subjects.add(Objects.requireNonNull(this.subjects.get(id.toLowerCase(Locale.ROOT)))); } return CompletableFuture.completedFuture(subjects.build()); } From 5735e0ef44998ab5aa4d273655953ed6f25a9d4e Mon Sep 17 00:00:00 2001 From: Emily <35617540+emilyy-dev@users.noreply.github.com> Date: Sat, 4 Sep 2021 18:15:13 -0300 Subject: [PATCH 026/314] Fix fabric/build.gradle placeholder replacement (#3153) --- fabric/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric/build.gradle b/fabric/build.gradle index 24672bd2f..4b51f2bd6 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -36,7 +36,7 @@ dependencies { processResources { inputs.property 'version', project.ext.fullVersion - filesMatching('fabric-mod.json') { + filesMatching('**/fabric.mod.json') { expand 'version': project.ext.fullVersion } } From 4e2807cff5a47193f3968cc89ce5cf6b74b1a025 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 10 Sep 2021 18:32:13 +0100 Subject: [PATCH 027/314] Refactor mod container retrieval in Fabric mod --- .../lucko/luckperms/fabric/LPFabricBootstrap.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricBootstrap.java b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricBootstrap.java index 0f2423945..468c7d8dc 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricBootstrap.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricBootstrap.java @@ -62,8 +62,11 @@ public final class LPFabricBootstrap implements LuckPermsBootstrap, DedicatedServerModInitializer { private static final String MODID = "luckperms"; - private static final ModContainer MOD_CONTAINER = FabricLoader.getInstance().getModContainer(MODID) - .orElseThrow(() -> new RuntimeException("Could not get the LuckPerms mod container.")); + + /** + * The mod container + */ + private final ModContainer modContainer; /** * The plugin logger @@ -100,6 +103,8 @@ public final class LPFabricBootstrap implements LuckPermsBootstrap, DedicatedSer private MinecraftServer server; public LPFabricBootstrap() { + this.modContainer = FabricLoader.getInstance().getModContainer(MODID) + .orElseThrow(() -> new RuntimeException("Could not get the LuckPerms mod container.")); this.logger = new Log4jPluginLogger(LogManager.getLogger(MODID)); this.schedulerAdapter = new FabricSchedulerAdapter(this); this.classPathAppender = new FabricClassPathAppender(); @@ -171,7 +176,7 @@ public Optional getServer() { @Override public String getVersion() { - return MOD_CONTAINER.getMetadata().getVersion().getFriendlyString(); + return this.modContainer.getMetadata().getVersion().getFriendlyString(); } @Override @@ -217,7 +222,7 @@ public Path getConfigDirectory() { @Override public InputStream getResourceStream(String path) { try { - return Files.newInputStream(LPFabricBootstrap.MOD_CONTAINER.getPath(path)); + return Files.newInputStream(this.modContainer.getPath(path)); } catch (IOException e) { return null; } From d1cb644712a4baba450f541978b5e8effaaac598 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 10 Sep 2021 19:14:47 +0100 Subject: [PATCH 028/314] Fix empty prefix/suffix strings in YAML storage (#3139, #3160) --- .../file/AbstractConfigurateStorage.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java index 7cfe2934f..e812e83ad 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java @@ -439,11 +439,12 @@ private static NodeEntry parseNode(ConfigurationNode configNode, String keyField } // assume 'configNode' is the actual entry. - String permission = children.get(keyFieldName).getString(null); - if (permission == null || permission.isEmpty()) { + ConfigurationNode appended = children.get(keyFieldName); + if (appended == null) { return null; } + String permission = appended.getString(""); return new NodeEntry(permission, configNode); } @@ -458,7 +459,7 @@ protected static Set readNodes(ConfigurationNode data) { } NodeEntry entry = parseNode(appended, "permission"); - if (entry == null) { + if (entry == null || entry.key.isEmpty()) { continue; } @@ -476,7 +477,7 @@ protected static Set readNodes(ConfigurationNode data) { } NodeEntry entry = parseNode(appended, "group"); - if (entry == null) { + if (entry == null || entry.key.isEmpty()) { continue; } @@ -512,7 +513,7 @@ protected static Set readNodes(ConfigurationNode data) { for (ConfigurationNode appended : data.getNode("meta").getChildrenList()) { NodeEntry entry = parseNode(appended, "key"); - if (entry == null) { + if (entry == null || entry.key.isEmpty()) { continue; } @@ -545,7 +546,7 @@ private static boolean isPlain(Node node) { private void appendNode(ConfigurationNode base, String key, ConfigurationNode attributes, String keyFieldName) { ConfigurationNode appended = base.appendListNode(); - if (this.loader instanceof YamlLoader) { + if (this.loader instanceof YamlLoader && !key.isEmpty()) { // create a map node with a single entry of key --> attributes appended.getNode(key).setValue(attributes); } else { From 2cf77db8b639f494f2060c1ffd6f7748136400f3 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 18 Sep 2021 17:30:17 +0100 Subject: [PATCH 029/314] TranslationManager fixes and improvements (#3165) --- .../commands/misc/TranslationsCommand.java | 9 ++++---- .../common/locale/TranslationManager.java | 21 ++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/TranslationsCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/TranslationsCommand.java index 76061e9d4..052031cb9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/TranslationsCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/TranslationsCommand.java @@ -40,6 +40,7 @@ import net.kyori.adventure.text.Component; import java.io.IOException; +import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; @@ -70,12 +71,12 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St return; } - Message.INSTALLED_TRANSLATIONS.send(sender, plugin.getTranslationManager().getInstalledLocales().stream().map(Locale::toString).collect(Collectors.toList())); + Message.INSTALLED_TRANSLATIONS.send(sender, plugin.getTranslationManager().getInstalledLocales().stream().map(Locale::toLanguageTag).sorted().collect(Collectors.toList())); Message.AVAILABLE_TRANSLATIONS_HEADER.send(sender); - for (LanguageInfo language : availableTranslations) { - Message.AVAILABLE_TRANSLATIONS_ENTRY.send(sender, language.locale().toString(), TranslationManager.localeDisplayName(language.locale()), language.progress(), language.contributors()); - } + availableTranslations.stream() + .sorted(Comparator.comparing(language -> language.locale().toLanguageTag())) + .forEach(language -> Message.AVAILABLE_TRANSLATIONS_ENTRY.send(sender, language.locale().toLanguageTag(), TranslationManager.localeDisplayName(language.locale()), language.progress(), language.contributors())); sender.sendMessage(Message.prefixed(Component.empty())); Message.TRANSLATIONS_DOWNLOAD_PROMPT.send(sender, label); } diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/TranslationManager.java b/common/src/main/java/me/lucko/luckperms/common/locale/TranslationManager.java index f14f83efb..d02094a57 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/TranslationManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/TranslationManager.java @@ -123,9 +123,10 @@ public void loadCustom() { for (Path translationFile : translationFiles) { try { Map.Entry result = loadCustomTranslationFile(translationFile); - if (result != null) { - loaded.put(result.getKey(), result.getValue()); - } + loaded.put(result.getKey(), result.getValue()); + } catch (IllegalArgumentException e) { + // common error is from adventure "java.lang.IllegalArgumentException: Invalid key" -- don't print the whole stack trace. + this.plugin.getLogger().warn("Error loading locale file: " + translationFile.getFileName() + " - " + e); } catch (Exception e) { this.plugin.getLogger().warn("Error loading locale file: " + translationFile.getFileName(), e); } @@ -135,27 +136,27 @@ public void loadCustom() { loaded.forEach((locale, bundle) -> { Locale localeWithoutCountry = new Locale(locale.getLanguage()); if (!locale.equals(localeWithoutCountry) && !localeWithoutCountry.equals(DEFAULT_LOCALE) && this.installed.add(localeWithoutCountry)) { - this.registry.registerAll(localeWithoutCountry, bundle, false); + try { + this.registry.registerAll(localeWithoutCountry, bundle, false); + } catch (IllegalArgumentException e) { + // ignore "IllegalArgumentException: Invalid key" from adventure TranslationRegistry + } } }); } - private Map.Entry loadCustomTranslationFile(Path translationFile) { + private Map.Entry loadCustomTranslationFile(Path translationFile) throws IOException { String fileName = translationFile.getFileName().toString(); String localeString = fileName.substring(0, fileName.length() - ".properties".length()); Locale locale = parseLocale(localeString); if (locale == null) { - this.plugin.getLogger().warn("Unknown locale '" + localeString + "' - unable to register."); - return null; + throw new IllegalStateException("Unknown locale '" + localeString + "' - unable to register."); } PropertyResourceBundle bundle; try (BufferedReader reader = Files.newBufferedReader(translationFile, StandardCharsets.UTF_8)) { bundle = new PropertyResourceBundle(reader); - } catch(IOException e) { - this.plugin.getLogger().warn("Error loading locale file: " + localeString, e); - return null; } this.registry.registerAll(locale, bundle, false); From 04bb035a83af96d55e7dffea514c505c66ce0f54 Mon Sep 17 00:00:00 2001 From: RedstoneFuture Date: Sun, 3 Oct 2021 19:55:38 +0200 Subject: [PATCH 030/314] Add warning nore about enable-ops to config files (#3170) --- bukkit/src/main/resources/config.yml | 3 ++- fabric/src/main/resources/luckperms.conf | 3 ++- nukkit/src/main/resources/config.yml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index 3dd7fa872..eb8d1c57a 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -585,7 +585,8 @@ group-weight: # Controls whether server operators should exist at all. # # - When set to 'false', all players will be de-opped, and the /op and /deop commands will be -# disabled. +# disabled. Note that vanilla features like the spawn-protection require an operator on the +# server to work. enable-ops: true # Enables or disables a special permission based system in LuckPerms for controlling OP status. diff --git a/fabric/src/main/resources/luckperms.conf b/fabric/src/main/resources/luckperms.conf index f8769ebc8..2ffa927eb 100644 --- a/fabric/src/main/resources/luckperms.conf +++ b/fabric/src/main/resources/luckperms.conf @@ -572,7 +572,8 @@ group-weight { # Controls whether server operators should exist at all. # # - When set to 'false', all players will be de-opped, and the /op and /deop commands will be -# disabled. +# disabled. Note that vanilla features like the spawn-protection require an operator on the +# server to work. enable-ops = true # Enables or disables a special permission based system in LuckPerms for controlling OP status. diff --git a/nukkit/src/main/resources/config.yml b/nukkit/src/main/resources/config.yml index 574ebed59..8f6dfa7f0 100644 --- a/nukkit/src/main/resources/config.yml +++ b/nukkit/src/main/resources/config.yml @@ -580,7 +580,8 @@ group-weight: # Controls whether server operators should exist at all. # # - When set to 'false', all players will be de-opped, and the /op and /deop commands will be -# disabled. +# disabled. Note that vanilla features like the spawn-protection require an operator on the +# server to work. enable-ops: true # Enables or disables a special permission based system in LuckPerms for controlling OP status. From b2c76aca7da8751a2927bac534d76253fbed0e1c Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 10 Oct 2021 13:26:33 +0100 Subject: [PATCH 031/314] Improve translations handling (#3166) --- .../common/locale/TranslationManager.java | 66 +++++++++++--- .../common/locale/TranslationRepository.java | 91 +++++++++++-------- 2 files changed, 105 insertions(+), 52 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/TranslationManager.java b/common/src/main/java/me/lucko/luckperms/common/locale/TranslationManager.java index d02094a57..f574107ac 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/TranslationManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/TranslationManager.java @@ -28,6 +28,7 @@ import com.google.common.collect.Maps; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.util.MoreFiles; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; @@ -60,19 +61,39 @@ public class TranslationManager { public static final Locale DEFAULT_LOCALE = Locale.ENGLISH; private final LuckPermsPlugin plugin; - private final Path translationsDirectory; private final Set installed = ConcurrentHashMap.newKeySet(); private TranslationRegistry registry; + private final Path translationsDirectory; + private final Path repositoryTranslationsDirectory; + private final Path customTranslationsDirectory; + public TranslationManager(LuckPermsPlugin plugin) { this.plugin = plugin; this.translationsDirectory = this.plugin.getBootstrap().getConfigDirectory().resolve("translations"); + this.repositoryTranslationsDirectory = this.translationsDirectory.resolve("repository"); + this.customTranslationsDirectory = this.translationsDirectory.resolve("custom"); + + try { + MoreFiles.createDirectoriesIfNotExists(this.repositoryTranslationsDirectory); + MoreFiles.createDirectoriesIfNotExists(this.customTranslationsDirectory); + } catch (IOException e) { + // ignore + } } public Path getTranslationsDirectory() { return this.translationsDirectory; } + public Path getRepositoryTranslationsDirectory() { + return this.repositoryTranslationsDirectory; + } + + public Path getRepositoryStatusFile() { + return this.repositoryTranslationsDirectory.resolve("status.json"); + } + public Set getInstalledLocales() { return Collections.unmodifiableSet(this.installed); } @@ -89,8 +110,9 @@ public void reload() { this.registry.defaultLocale(DEFAULT_LOCALE); // load custom translations first, then the base (built-in) translations after. - loadCustom(); - loadBase(); + loadFromFileSystem(this.customTranslationsDirectory, false); + loadFromFileSystem(this.repositoryTranslationsDirectory, true); + loadFromResourceBundle(); // register it to the global source, so our translations can be picked up by adventure-platform GlobalTranslator.get().addSource(this.registry); @@ -99,36 +121,45 @@ public void reload() { /** * Loads the base (English) translations from the jar file. */ - private void loadBase() { + private void loadFromResourceBundle() { ResourceBundle bundle = ResourceBundle.getBundle("luckperms", DEFAULT_LOCALE, UTF8ResourceBundleControl.get()); try { this.registry.registerAll(DEFAULT_LOCALE, bundle, false); } catch (IllegalArgumentException e) { - this.plugin.getLogger().warn("Error loading default locale file", e); + if (!isAdventureDuplicatesException(e)) { + this.plugin.getLogger().warn("Error loading default locale file", e); + } } } + public static boolean isTranslationFile(Path path) { + return path.getFileName().toString().endsWith(".properties"); + } + /** * Loads custom translations (in any language) from the plugin configuration folder. */ - public void loadCustom() { + public void loadFromFileSystem(Path directory, boolean suppressDuplicatesError) { List translationFiles; - try (Stream stream = Files.list(this.translationsDirectory)) { - translationFiles = stream.filter(path -> path.getFileName().toString().endsWith(".properties")).collect(Collectors.toList()); + try (Stream stream = Files.list(directory)) { + translationFiles = stream.filter(TranslationManager::isTranslationFile).collect(Collectors.toList()); } catch (IOException e) { translationFiles = Collections.emptyList(); } + if (translationFiles.isEmpty()) { + return; + } + Map loaded = new HashMap<>(); for (Path translationFile : translationFiles) { try { - Map.Entry result = loadCustomTranslationFile(translationFile); + Map.Entry result = loadTranslationFile(translationFile); loaded.put(result.getKey(), result.getValue()); - } catch (IllegalArgumentException e) { - // common error is from adventure "java.lang.IllegalArgumentException: Invalid key" -- don't print the whole stack trace. - this.plugin.getLogger().warn("Error loading locale file: " + translationFile.getFileName() + " - " + e); } catch (Exception e) { - this.plugin.getLogger().warn("Error loading locale file: " + translationFile.getFileName(), e); + if (!suppressDuplicatesError || !isAdventureDuplicatesException(e)) { + this.plugin.getLogger().warn("Error loading locale file: " + translationFile.getFileName(), e); + } } } @@ -139,13 +170,13 @@ public void loadCustom() { try { this.registry.registerAll(localeWithoutCountry, bundle, false); } catch (IllegalArgumentException e) { - // ignore "IllegalArgumentException: Invalid key" from adventure TranslationRegistry + // ignore } } }); } - private Map.Entry loadCustomTranslationFile(Path translationFile) throws IOException { + private Map.Entry loadTranslationFile(Path translationFile) throws IOException { String fileName = translationFile.getFileName().toString(); String localeString = fileName.substring(0, fileName.length() - ".properties".length()); Locale locale = parseLocale(localeString); @@ -164,6 +195,11 @@ private Map.Entry loadCustomTranslationFile(Path transla return Maps.immutableEntry(locale, bundle); } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + private static boolean isAdventureDuplicatesException(Exception e) { + return e instanceof IllegalArgumentException && (e.getMessage().startsWith("Invalid key") || e.getMessage().startsWith("Translation already exists")); + } + public static Component render(Component component) { return render(component, Locale.getDefault()); } diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/TranslationRepository.java b/common/src/main/java/me/lucko/luckperms/common/locale/TranslationRepository.java index b71a98bc7..f787c6ca9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/TranslationRepository.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/TranslationRepository.java @@ -33,7 +33,6 @@ import me.lucko.luckperms.common.http.UnsuccessfulRequestException; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.util.MoreFiles; import me.lucko.luckperms.common.util.gson.GsonProvider; import org.checkerframework.checker.nullness.qual.Nullable; @@ -59,6 +58,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; public class TranslationRepository { private static final String TRANSLATIONS_INFO_ENDPOINT = "https://metadata.luckperms.net/data/translations"; @@ -92,6 +92,9 @@ public void scheduleRefresh() { } this.plugin.getBootstrap().getScheduler().executeAsync(() -> { + // cleanup old translation files + clearDirectory(this.plugin.getTranslationManager().getTranslationsDirectory(), Files::isRegularFile); + try { refresh(); } catch (Exception e) { @@ -101,34 +104,14 @@ public void scheduleRefresh() { } private void refresh() throws Exception { - Path translationsDirectory = this.plugin.getTranslationManager().getTranslationsDirectory(); - try { - MoreFiles.createDirectoriesIfNotExists(translationsDirectory); - } catch (IOException e) { - // ignore - } - - long lastRefresh = 0L; - - Path repoStatusFile = translationsDirectory.resolve("repository.json"); - if (Files.exists(repoStatusFile)) { - try (BufferedReader reader = Files.newBufferedReader(repoStatusFile, StandardCharsets.UTF_8)) { - JsonObject status = GsonProvider.normal().fromJson(reader, JsonObject.class); - if (status.has("lastRefresh")) { - lastRefresh = status.get("lastRefresh").getAsLong(); - } - } catch (Exception e) { - // ignore - } - } - + long lastRefresh = readLastRefreshTime(); long timeSinceLastRefresh = System.currentTimeMillis() - lastRefresh; + if (timeSinceLastRefresh <= CACHE_MAX_AGE) { return; } MetadataResponse metadata = getTranslationsMetadata(); - if (timeSinceLastRefresh <= metadata.cacheMaxAge) { return; } @@ -137,6 +120,22 @@ private void refresh() throws Exception { downloadAndInstallTranslations(metadata.languages, null, true); } + private void clearDirectory(Path directory, Predicate predicate) { + try { + Files.list(directory) + .filter(predicate) + .forEach(p -> { + try { + Files.delete(p); + } catch (IOException e) { + // ignore + } + }); + } catch (IOException e) { + // ignore + } + } + /** * Downloads and installs translations for the given languages. * @@ -146,13 +145,10 @@ private void refresh() throws Exception { */ public void downloadAndInstallTranslations(List languages, @Nullable Sender sender, boolean updateStatus) { TranslationManager manager = this.plugin.getTranslationManager(); - Path translationsDirectory = manager.getTranslationsDirectory(); + Path translationsDirectory = manager.getRepositoryTranslationsDirectory(); - try { - MoreFiles.createDirectoriesIfNotExists(translationsDirectory); - } catch (IOException e) { - // ignore - } + // clear existing translations + clearDirectory(translationsDirectory, TranslationManager::isTranslationFile); for (LanguageInfo language : languages) { if (sender != null) { @@ -185,18 +181,39 @@ public void downloadAndInstallTranslations(List languages, @Nullab } if (updateStatus) { - // update status file - Path repoStatusFile = translationsDirectory.resolve("repository.json"); - try (BufferedWriter writer = Files.newBufferedWriter(repoStatusFile, StandardCharsets.UTF_8)) { - JsonObject status = new JsonObject(); - status.add("lastRefresh", new JsonPrimitive(System.currentTimeMillis())); - GsonProvider.prettyPrinting().toJson(status, writer); - } catch (IOException e) { + writeLastRefreshTime(); + } + + manager.reload(); + } + + private void writeLastRefreshTime() { + Path statusFile = this.plugin.getTranslationManager().getRepositoryStatusFile(); + + try (BufferedWriter writer = Files.newBufferedWriter(statusFile, StandardCharsets.UTF_8)) { + JsonObject status = new JsonObject(); + status.add("lastRefresh", new JsonPrimitive(System.currentTimeMillis())); + GsonProvider.prettyPrinting().toJson(status, writer); + } catch (IOException e) { + // ignore + } + } + + private long readLastRefreshTime() { + Path statusFile = this.plugin.getTranslationManager().getRepositoryStatusFile(); + + if (Files.exists(statusFile)) { + try (BufferedReader reader = Files.newBufferedReader(statusFile, StandardCharsets.UTF_8)) { + JsonObject status = GsonProvider.normal().fromJson(reader, JsonObject.class); + if (status.has("lastRefresh")) { + return status.get("lastRefresh").getAsLong(); + } + } catch (Exception e) { // ignore } } - this.plugin.getTranslationManager().reload(); + return 0L; } private MetadataResponse getTranslationsMetadata() throws IOException, UnsuccessfulRequestException { From 9c5a43b487dcb2f643a94354a9a7232195ddd581 Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 18 Oct 2021 22:10:20 +0100 Subject: [PATCH 032/314] Allow meta/prefix/suffix nodes to be negated to cancel inheritance (#3020) --- .../common/cacheddata/type/MetaAccumulator.java | 15 +++++++++++++++ .../luckperms/common/model/PermissionHolder.java | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java index e449ae0e4..fd6dfa8b9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java @@ -40,8 +40,10 @@ import net.luckperms.api.node.types.SuffixNode; import java.util.Comparator; +import java.util.HashSet; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicReference; @@ -79,6 +81,8 @@ private enum State { private int weight = 0; private String primaryGroup; + private Set seenNodeKeys = new HashSet<>(); + private final MetaStackDefinition prefixDefinition; private final MetaStackDefinition suffixDefinition; private final MetaStackAccumulator prefixAccumulator; @@ -120,6 +124,7 @@ public void complete() { if (this.primaryGroup != null && !this.meta.containsKey("primarygroup")) { this.meta.put("primarygroup", this.primaryGroup); } + this.seenNodeKeys = null; // free up for GC this.state.set(State.COMPLETE); } @@ -129,6 +134,16 @@ public void complete() { public void accumulateNode(Node n) { ensureState(State.ACCUMULATING); + // only process distinct nodes once, allows inheritance to be + // "cancelled out" by assigning a false copy. + if (!this.seenNodeKeys.add(n.getKey())) { + return; + } + + if (!n.getValue()) { + return; + } + if (n instanceof MetaNode) { MetaNode mn = (MetaNode) n; this.meta.put(mn.getMetaKey(), mn.getMetaValue()); diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index edd34ef30..4583261f0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -394,7 +394,7 @@ public MetaAccumulator accumulateMeta(MetaAccumulator accumulator, QueryOptions // accumulate nodes for (DataType dataType : holder.queryOrder(queryOptions)) { holder.getData(dataType).forEach(queryOptions, node -> { - if (node.getValue() && NodeType.META_OR_CHAT_META.matches(node)) { + if (NodeType.META_OR_CHAT_META.matches(node)) { accumulator.accumulateNode(node); } }); From f84f4258179c727125636c4e49aab7c5da83eaec Mon Sep 17 00:00:00 2001 From: Larry Date: Fri, 22 Oct 2021 12:58:12 -0600 Subject: [PATCH 033/314] Better issue templates (#3184) --- .github/ISSUE_TEMPLATE/bug-report.md | 40 ----------------- .github/ISSUE_TEMPLATE/bug-report.yml | 64 +++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/suggestion.md | 16 ------- .github/ISSUE_TEMPLATE/suggestion.yml | 32 ++++++++++++++ 4 files changed, 96 insertions(+), 56 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml delete mode 100644 .github/ISSUE_TEMPLATE/suggestion.md create mode 100644 .github/ISSUE_TEMPLATE/suggestion.yml diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md deleted file mode 100644 index 4754ae307..000000000 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: 🐛 ​ Bug report -about: Report an issue or problem with the way LuckPerms is functioning. -title: '' -labels: 'type: issue' -assignees: '' - ---- - - - -### Description - - - -### Reproduction steps - - -1. Open the config.yml file and set example to true. -2. Restart the server -3. Run /lp user example permission set x from the console -4. See error - - -### Expected behaviour - - - -### Environment details - - -* Server type/version: `ExampleSpigot` running version `1.12.2` build `???` -* LuckPerms version: `v???` - - - - -### Any other relevant details - - diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 000000000..3f889819e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,64 @@ +name: Bug Report +description: Report an issue or problem with the way LuckPerms is functioning. +title: "Bug Report: " +labels: ["type: issue"] +body: + - type: markdown + attributes: + value: "Before going any further, please check to see if your problem is fixed by updating to a newer version. The latest development builds can be obtained from https://luckperms.net/download" + - type: textarea + id: description + attributes: + label: Description + description: Please provide a short description of the issue in the space below. + placeholder: Description goes here! + validations: + required: true + - type: textarea + id: reproduction-steps + attributes: + label: Reproduction Steps + description: Please provide a clear list of steps we should follow in order to reproduce your issue in the space below. + placeholder: List reproduction steps here, please! + validations: + required: true + - type: textarea + id: expected-behaviour + attributes: + label: Expected Behaviour + description: Please provide a description of what you expected to happen. + placeholder: Describe what you expected to happen here. + validations: + required: true + - type: input + id: server-info + attributes: + label: Server Details + description: Please provide a description of the server details, including type and specific version number. + placeholder: 'git-Paper-124 (MC: 1.17.1)' + validations: + required: true + - type: input + id: luckperms-version + attributes: + label: LuckPerms Version + description: Please provide the specific, precise version number of LuckPerms you are using to reproduce this bug. + placeholder: v5.3.70 + validations: + required: true + - type: textarea + id: logs + attributes: + label: Logs and Configs + description: If you think it would be helpful, please also include a pastebin of any relevant config files or log output. You should use https://gist.github.com/ to upload, then include the link below. + placeholder: https://gist.github.com/HelpfulBugReporter/ThisIsAVeryUsefulLogOutput + validations: + required: false + - type: textarea + id: extra-info + attributes: + label: Extra Details + description: Please include any other relevant details in the space below. + placeholder: I was able to reproduce this only at the end of a rainbow. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/suggestion.md b/.github/ISSUE_TEMPLATE/suggestion.md deleted file mode 100644 index cae60ae12..000000000 --- a/.github/ISSUE_TEMPLATE/suggestion.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: 💡 ​ Suggestion -about: Suggest an idea for an improvement or change to LuckPerms. -title: '' -labels: 'type: suggestion' -assignees: '' - ---- - -### Description - - - -### Proposed behaviour - - diff --git a/.github/ISSUE_TEMPLATE/suggestion.yml b/.github/ISSUE_TEMPLATE/suggestion.yml new file mode 100644 index 000000000..134879596 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/suggestion.yml @@ -0,0 +1,32 @@ +name: Suggestion +description: Suggest an idea for an improvement or change to LuckPerms. +title: "Suggestion: " +labels: ["type: suggestion"] +body: + - type: markdown + attributes: + value: "Before going any further, please check to see if your suggestion has already been added by checking the [wiki](https://luckperms.net/wiki/Home). The latest development builds can be obtained from https://luckperms.net/download" + - type: textarea + id: description + attributes: + label: Description + description: Please provide a short description of your suggestion in the space below. + placeholder: Hello, perhaps you should... + validations: + required: true + - type: textarea + id: proposed-behaviour + attributes: + label: Proposed Behaviour + description: Please provide a short explanation of how the feature should work / be changed, and how this will affect the project. + placeholder: It would... + validations: + required: true + - type: textarea + id: extra-info + attributes: + label: Extra Details + description: Please include any other relevant details in the space below. + placeholder: I think this should only function at the end of a rainbow. + validations: + required: false From 00b62d7cd9a313f7e1c221fe1f55531da98eede6 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 22 Oct 2021 20:01:46 +0100 Subject: [PATCH 034/314] Edit issue templates --- .github/ISSUE_TEMPLATE/bug-report.yml | 4 ++-- .github/ISSUE_TEMPLATE/suggestion.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 3f889819e..1f927aff7 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,6 +1,6 @@ -name: Bug Report +name: 🐛 ​ Bug report description: Report an issue or problem with the way LuckPerms is functioning. -title: "Bug Report: " +title: "" labels: ["type: issue"] body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/suggestion.yml b/.github/ISSUE_TEMPLATE/suggestion.yml index 134879596..d23f844e4 100644 --- a/.github/ISSUE_TEMPLATE/suggestion.yml +++ b/.github/ISSUE_TEMPLATE/suggestion.yml @@ -1,6 +1,6 @@ -name: Suggestion +name: 💡 ​ Suggestion description: Suggest an idea for an improvement or change to LuckPerms. -title: "Suggestion: " +title: "" labels: ["type: suggestion"] body: - type: markdown From f896ed723dc944acd4a823db982ad48c5fc4840d Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 22 Oct 2021 20:03:33 +0100 Subject: [PATCH 035/314] Edit issue templates (again) --- .github/ISSUE_TEMPLATE/bug-report.yml | 1 - .github/ISSUE_TEMPLATE/suggestion.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 1f927aff7..fc6881422 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,6 +1,5 @@ name: 🐛 ​ Bug report description: Report an issue or problem with the way LuckPerms is functioning. -title: "" labels: ["type: issue"] body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/suggestion.yml b/.github/ISSUE_TEMPLATE/suggestion.yml index d23f844e4..12e8852ba 100644 --- a/.github/ISSUE_TEMPLATE/suggestion.yml +++ b/.github/ISSUE_TEMPLATE/suggestion.yml @@ -1,6 +1,5 @@ name: 💡 ​ Suggestion description: Suggest an idea for an improvement or change to LuckPerms. -title: "" labels: ["type: suggestion"] body: - type: markdown From 848fc353d4498f1e65c41f9889dc565ba5aadefd Mon Sep 17 00:00:00 2001 From: Eduard Wayland <12481684+EduardWayland@users.noreply.github.com> Date: Fri, 12 Nov 2021 23:49:46 +0100 Subject: [PATCH 036/314] Added redis authentication with username (#3201) --- bukkit/src/main/resources/config.yml | 1 + bungee/src/main/resources/config.yml | 1 + .../java/me/lucko/luckperms/common/config/ConfigKeys.java | 5 +++++ .../luckperms/common/messaging/MessagingFactory.java | 6 +++++- .../luckperms/common/messaging/redis/RedisMessenger.java | 8 ++++++-- fabric/src/main/resources/luckperms.conf | 1 + nukkit/src/main/resources/config.yml | 1 + sponge/src/main/resources/luckperms.conf | 1 + velocity/src/main/resources/config.yml | 1 + 9 files changed, 22 insertions(+), 3 deletions(-) diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index eb8d1c57a..a4dd153e7 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -262,6 +262,7 @@ broadcast-received-log-entries: true redis: enabled: false address: localhost + username: '' password: '' # Settings for RabbitMQ. diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index 1b93a0d78..a3b5a19f7 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -260,6 +260,7 @@ broadcast-received-log-entries: false redis: enabled: false address: localhost + username: '' password: '' # Settings for RabbitMQ. diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 1aa8d32e7..14c97cab3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -632,6 +632,11 @@ private ConfigKeys() {} */ public static final ConfigKey REDIS_ADDRESS = notReloadable(stringKey("redis.address", null)); + /** + * The username to connect with, or an empty string if it should use default + */ + public static final ConfigKey REDIS_USERNAME = notReloadable(stringKey("redis.username", "")); + /** * The password in use by the redis server, or an empty string if there is no password */ diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java b/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java index db92439e9..4e369f23d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/MessagingFactory.java @@ -140,13 +140,17 @@ private class RedisMessengerProvider implements MessengerProvider { LuckPermsConfiguration config = getPlugin().getConfiguration(); String address = config.get(ConfigKeys.REDIS_ADDRESS); + String username = config.get(ConfigKeys.REDIS_USERNAME); String password = config.get(ConfigKeys.REDIS_PASSWORD); if (password.isEmpty()) { password = null; } + if (username.isEmpty()) { + username = null; + } boolean ssl = config.get(ConfigKeys.REDIS_SSL); - redis.init(address, password, ssl); + redis.init(address, username, password, ssl); return redis; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java b/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java index 859a93ed4..73cf911d9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java @@ -56,12 +56,16 @@ public RedisMessenger(LuckPermsPlugin plugin, IncomingMessageConsumer consumer) this.consumer = consumer; } - public void init(String address, String password, boolean ssl) { + public void init(String address, String username, String password, boolean ssl) { String[] addressSplit = address.split(":"); String host = addressSplit[0]; int port = addressSplit.length > 1 ? Integer.parseInt(addressSplit[1]) : Protocol.DEFAULT_PORT; - this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, password, ssl); + if (username == null) { + this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, password, ssl); + } else { + this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, username, password, ssl); + } this.sub = new Subscription(); this.plugin.getBootstrap().getScheduler().executeAsync(this.sub); diff --git a/fabric/src/main/resources/luckperms.conf b/fabric/src/main/resources/luckperms.conf index 2ffa927eb..8d3686dc8 100644 --- a/fabric/src/main/resources/luckperms.conf +++ b/fabric/src/main/resources/luckperms.conf @@ -265,6 +265,7 @@ broadcast-received-log-entries = true redis { enabled = false address = "localhost" + username = "" password = "" } diff --git a/nukkit/src/main/resources/config.yml b/nukkit/src/main/resources/config.yml index 8f6dfa7f0..e6cb4d832 100644 --- a/nukkit/src/main/resources/config.yml +++ b/nukkit/src/main/resources/config.yml @@ -257,6 +257,7 @@ broadcast-received-log-entries: true redis: enabled: false address: localhost + username: '' password: '' # Settings for RabbitMQ. diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 764cac2f8..ae22acfd5 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -265,6 +265,7 @@ broadcast-received-log-entries = true redis { enabled = false address = "localhost" + username = "" password = "" } diff --git a/velocity/src/main/resources/config.yml b/velocity/src/main/resources/config.yml index 56f8799a3..7f82c0e09 100644 --- a/velocity/src/main/resources/config.yml +++ b/velocity/src/main/resources/config.yml @@ -251,6 +251,7 @@ broadcast-received-log-entries: false redis: enabled: false address: localhost + username: '' password: '' # Settings for RabbitMQ. From c9d4c7dac7ec868d4c4efdf41970bc2f883a2921 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 17 Nov 2021 23:18:11 +0000 Subject: [PATCH 037/314] ContextSet performance improvements (#3209) A bit experimental, should be stable enough though. --- .../api/context/ImmutableContextSet.java | 2 +- .../api/context/MutableContextSet.java | 2 +- .../bukkit/context/BukkitContextManager.java | 4 +- .../context/BukkitPlayerCalculator.java | 2 +- .../permissible/LuckPermsPermissible.java | 2 +- .../bukkit/vault/LuckPermsVaultChat.java | 2 +- .../bungee/context/BungeeContextManager.java | 4 +- .../context/BungeePlayerCalculator.java | 2 +- .../bungee/context/RedisBungeeCalculator.java | 2 +- .../api/implementation/ApiContextManager.java | 2 +- .../implementation/ApiContextSetFactory.java | 4 +- .../api/implementation/ApiPlayerAdapter.java | 2 +- .../common/command/utils/ArgumentList.java | 4 +- .../commands/generic/other/HolderEditor.java | 2 +- .../parent/UserSwitchPrimaryGroup.java | 2 +- .../common/commands/log/LogNotify.java | 2 +- .../common/commands/misc/EditorCommand.java | 2 +- .../common/commands/track/TrackEditor.java | 2 +- .../luckperms/common/config/ConfigKeys.java | 2 +- .../luckperms/common/config/ContextsFile.java | 4 +- .../{contextset => }/AbstractContextSet.java | 58 +---- .../context/{contextset => }/ContextImpl.java | 11 +- .../ImmutableContextSetImpl.java | 208 +++++++++++------- .../MutableContextSetImpl.java | 94 ++++---- .../ConfigurationContextCalculator.java | 4 +- .../ForwardingContextCalculator.java | 2 +- .../{ => calculator}/WorldNameRewriter.java | 2 +- .../context/comparator/ContextComparator.java | 75 +++++++ .../ContextSetComparator.java | 27 +-- .../context/{ => manager}/ContextManager.java | 5 +- .../{ => manager}/QueryOptionsCache.java | 2 +- .../{ => manager}/QueryOptionsSupplier.java | 2 +- .../ContextSetConfigurateSerializer.java | 6 +- .../ContextSetJsonSerializer.java | 6 +- .../manager/user/AbstractUserManager.java | 2 +- .../common/model/nodemap/NodeMapMutable.java | 2 +- .../common/node/AbstractNodeBuilder.java | 2 +- .../comparator/NodeWithContextComparator.java | 2 +- .../common/node/utils/NodeJsonSerializer.java | 2 +- .../plugin/AbstractLuckPermsPlugin.java | 2 +- .../common/plugin/LuckPermsPlugin.java | 2 +- .../common/query/QueryOptionsBuilderImpl.java | 2 +- .../common/query/QueryOptionsImpl.java | 2 +- .../lucko/luckperms/common/sender/Sender.java | 2 +- .../file/AbstractConfigurateStorage.java | 4 +- .../implementation/mongodb/MongoStorage.java | 2 +- .../implementation/sql/SqlStorage.java | 2 +- .../common/webeditor/WebEditorRequest.java | 4 +- .../common/webeditor/WebEditorResponse.java | 2 +- .../common/context/ContextSetTest.java | 158 +++++++++++++ .../fabric/context/FabricContextManager.java | 4 +- .../context/FabricPlayerCalculator.java | 2 +- .../fabric/mixin/ServerPlayerEntityMixin.java | 2 +- .../luckperms/fabric/model/MixinUser.java | 2 +- .../nukkit/context/NukkitContextManager.java | 4 +- .../context/NukkitPlayerCalculator.java | 2 +- .../permissible/LuckPermsPermissible.java | 2 +- .../proxy/api6/DescriptionBuilder.java | 2 +- .../service/proxy/api6/SubjectProxy.java | 2 +- .../proxy/api7/DescriptionBuilder.java | 2 +- .../service/proxy/api7/SubjectProxy.java | 2 +- .../sponge/service/CompatibilityUtil.java | 4 +- .../service/model/LPPermissionService.java | 2 +- .../sponge/context/SpongeContextManager.java | 6 +- .../context/SpongePlayerCalculator.java | 2 +- .../model/manager/SpongeGroupManager.java | 2 +- .../model/manager/SpongeUserManager.java | 2 +- .../sponge/service/LuckPermsService.java | 2 +- .../service/model/ContextCalculatorProxy.java | 2 +- .../calculated/CalculatedSubjectData.java | 2 +- .../model/persisted/PersistedCollection.java | 2 +- .../model/persisted/SubjectDataContainer.java | 4 +- .../context/VelocityContextManager.java | 6 +- .../context/VelocityPlayerCalculator.java | 2 +- .../service/PlayerPermissionProvider.java | 2 +- 75 files changed, 523 insertions(+), 287 deletions(-) rename common/src/main/java/me/lucko/luckperms/common/context/{contextset => }/AbstractContextSet.java (68%) rename common/src/main/java/me/lucko/luckperms/common/context/{contextset => }/ContextImpl.java (86%) rename common/src/main/java/me/lucko/luckperms/common/context/{contextset => }/ImmutableContextSetImpl.java (52%) rename common/src/main/java/me/lucko/luckperms/common/context/{contextset => }/MutableContextSetImpl.java (81%) rename common/src/main/java/me/lucko/luckperms/common/context/{ => calculator}/ConfigurationContextCalculator.java (95%) rename common/src/main/java/me/lucko/luckperms/common/context/{ => calculator}/ForwardingContextCalculator.java (96%) rename common/src/main/java/me/lucko/luckperms/common/context/{ => calculator}/WorldNameRewriter.java (98%) create mode 100644 common/src/main/java/me/lucko/luckperms/common/context/comparator/ContextComparator.java rename common/src/main/java/me/lucko/luckperms/common/context/{ => comparator}/ContextSetComparator.java (83%) rename common/src/main/java/me/lucko/luckperms/common/context/{ => manager}/ContextManager.java (97%) rename common/src/main/java/me/lucko/luckperms/common/context/{ => manager}/QueryOptionsCache.java (97%) rename common/src/main/java/me/lucko/luckperms/common/context/{ => manager}/QueryOptionsSupplier.java (96%) rename common/src/main/java/me/lucko/luckperms/common/context/{ => serializer}/ContextSetConfigurateSerializer.java (94%) rename common/src/main/java/me/lucko/luckperms/common/context/{ => serializer}/ContextSetJsonSerializer.java (95%) create mode 100644 common/src/test/java/me/lucko/luckperms/common/context/ContextSetTest.java diff --git a/api/src/main/java/net/luckperms/api/context/ImmutableContextSet.java b/api/src/main/java/net/luckperms/api/context/ImmutableContextSet.java index b2312fa52..ef2947ff2 100644 --- a/api/src/main/java/net/luckperms/api/context/ImmutableContextSet.java +++ b/api/src/main/java/net/luckperms/api/context/ImmutableContextSet.java @@ -119,7 +119,7 @@ interface Builder { } /** - * Adds of of the contexts in another {@link ContextSet} to the set. + * Adds all the contexts in another {@link ContextSet} to the set. * * @param contextSet the set to add from * @return the builder diff --git a/api/src/main/java/net/luckperms/api/context/MutableContextSet.java b/api/src/main/java/net/luckperms/api/context/MutableContextSet.java index 83bc91384..dca79d999 100644 --- a/api/src/main/java/net/luckperms/api/context/MutableContextSet.java +++ b/api/src/main/java/net/luckperms/api/context/MutableContextSet.java @@ -94,7 +94,7 @@ default void addAll(@NonNull Iterable iterable) { } /** - * Adds of of the contexts in another {@link ContextSet} to this set. + * Adds all the contexts in another {@link ContextSet} to this set. * * @param contextSet the set to add from * @throws NullPointerException if the contextSet is null 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 738b3cfef..2065f2585 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 @@ -30,8 +30,8 @@ import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.common.cache.LoadingMap; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.ContextManager; -import me.lucko.luckperms.common.context.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.ContextManager; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.util.CaffeineFactory; import net.luckperms.api.context.ImmutableContextSet; diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/BukkitPlayerCalculator.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/BukkitPlayerCalculator.java index 15ab0693e..0b3dcb224 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/BukkitPlayerCalculator.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/BukkitPlayerCalculator.java @@ -29,7 +29,7 @@ import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.util.EnumNamer; import net.luckperms.api.context.Context; diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissible.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissible.java index 8958aad92..164b8d658 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissible.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissible.java @@ -33,7 +33,7 @@ import me.lucko.luckperms.bukkit.calculator.OpProcessor; import me.lucko.luckperms.common.calculator.result.TristateResult; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultChat.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultChat.java index 470ae6122..c77d3016c 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultChat.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultChat.java @@ -30,7 +30,7 @@ import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator; import me.lucko.luckperms.common.cacheddata.type.MetaCache; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.node.types.Meta; 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 3050dbdc0..73bf1112d 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 @@ -28,8 +28,8 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import me.lucko.luckperms.bungee.LPBungeePlugin; -import me.lucko.luckperms.common.context.ContextManager; -import me.lucko.luckperms.common.context.QueryOptionsSupplier; +import me.lucko.luckperms.common.context.manager.ContextManager; +import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.common.util.CaffeineFactory; import net.luckperms.api.context.ImmutableContextSet; diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/context/BungeePlayerCalculator.java b/bungee/src/main/java/me/lucko/luckperms/bungee/context/BungeePlayerCalculator.java index 278a911b2..902527a3a 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/context/BungeePlayerCalculator.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/context/BungeePlayerCalculator.java @@ -27,7 +27,7 @@ import me.lucko.luckperms.bungee.LPBungeePlugin; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import net.luckperms.api.context.ContextCalculator; import net.luckperms.api.context.ContextConsumer; diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/context/RedisBungeeCalculator.java b/bungee/src/main/java/me/lucko/luckperms/bungee/context/RedisBungeeCalculator.java index 682d6dd78..0a610bec1 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/context/RedisBungeeCalculator.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/context/RedisBungeeCalculator.java @@ -28,7 +28,7 @@ import com.imaginarycode.minecraft.redisbungee.RedisBungee; import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import net.luckperms.api.context.ContextConsumer; import net.luckperms.api.context.ContextSet; 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 58d7bf3d0..7d6031e05 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 @@ -25,7 +25,7 @@ package me.lucko.luckperms.common.api.implementation; -import me.lucko.luckperms.common.context.ContextManager; +import me.lucko.luckperms.common.context.manager.ContextManager; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.query.QueryOptionsBuilderImpl; diff --git a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiContextSetFactory.java b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiContextSetFactory.java index 55c78a7c2..becd8bc8e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiContextSetFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiContextSetFactory.java @@ -25,8 +25,8 @@ package me.lucko.luckperms.common.api.implementation; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; -import me.lucko.luckperms.common.context.contextset.MutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.MutableContextSetImpl; import net.luckperms.api.context.ContextSetFactory; import net.luckperms.api.context.ImmutableContextSet; diff --git a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlayerAdapter.java b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlayerAdapter.java index f9d4a62ab..9c9d5d80d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlayerAdapter.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlayerAdapter.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.common.api.implementation; -import me.lucko.luckperms.common.context.ContextManager; +import me.lucko.luckperms.common.context.manager.ContextManager; import me.lucko.luckperms.common.model.manager.user.UserManager; import net.luckperms.api.context.ImmutableContextSet; diff --git a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentList.java b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentList.java index 09216ee66..14b663a5b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentList.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentList.java @@ -29,8 +29,8 @@ import me.lucko.luckperms.common.command.abstraction.CommandException; import me.lucko.luckperms.common.commands.user.UserParentCommand; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; -import me.lucko.luckperms.common.context.contextset.MutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.MutableContextSetImpl; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.DurationParser; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java index bca8c8f44..0dfaa0da2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java @@ -30,7 +30,7 @@ import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.command.spec.CommandSpec; import me.lucko.luckperms.common.command.utils.ArgumentList; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.HolderType; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/UserSwitchPrimaryGroup.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/UserSwitchPrimaryGroup.java index 41b2c6efb..f798948b8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/UserSwitchPrimaryGroup.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/UserSwitchPrimaryGroup.java @@ -35,7 +35,7 @@ import me.lucko.luckperms.common.command.utils.ArgumentList; import me.lucko.luckperms.common.command.utils.StorageAssistant; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogNotify.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogNotify.java index abffbd33e..332d9f659 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogNotify.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogNotify.java @@ -30,7 +30,7 @@ import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.command.spec.CommandSpec; import me.lucko.luckperms.common.command.utils.ArgumentList; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.types.Permission; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java index 459003b41..d96eba560 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java @@ -30,7 +30,7 @@ import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.command.spec.CommandSpec; import me.lucko.luckperms.common.command.utils.ArgumentList; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.Track; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java index 77404b6d7..50af93903 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java @@ -30,7 +30,7 @@ import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.command.spec.CommandSpec; import me.lucko.luckperms.common.command.utils.ArgumentList; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 14c97cab3..7bd64e662 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -33,7 +33,7 @@ import me.lucko.luckperms.common.config.generic.KeyedConfiguration; import me.lucko.luckperms.common.config.generic.key.ConfigKey; import me.lucko.luckperms.common.config.generic.key.SimpleConfigKey; -import me.lucko.luckperms.common.context.WorldNameRewriter; +import me.lucko.luckperms.common.context.calculator.WorldNameRewriter; import me.lucko.luckperms.common.graph.TraversalAlgorithm; import me.lucko.luckperms.common.metastacking.SimpleMetaStackDefinition; import me.lucko.luckperms.common.metastacking.StandardStackElements; diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ContextsFile.java b/common/src/main/java/me/lucko/luckperms/common/config/ContextsFile.java index 851ca18e0..be59d1946 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ContextsFile.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ContextsFile.java @@ -28,8 +28,8 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; -import me.lucko.luckperms.common.context.ContextSetJsonSerializer; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.serializer.ContextSetJsonSerializer; import me.lucko.luckperms.common.util.gson.GsonProvider; import net.luckperms.api.context.ImmutableContextSet; diff --git a/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java b/common/src/main/java/me/lucko/luckperms/common/context/AbstractContextSet.java similarity index 68% rename from common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java rename to common/src/main/java/me/lucko/luckperms/common/context/AbstractContextSet.java index b61c52e53..0acc898c7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/AbstractContextSet.java @@ -23,11 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context.contextset; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterators; -import com.google.common.collect.SetMultimap; +package me.lucko.luckperms.common.context; import net.luckperms.api.context.Context; import net.luckperms.api.context.ContextSatisfyMode; @@ -36,36 +32,11 @@ import org.checkerframework.checker.nullness.qual.NonNull; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; import java.util.Locale; import java.util.Objects; -import java.util.Set; -import java.util.Spliterator; public abstract class AbstractContextSet implements ContextSet { - protected abstract SetMultimap backing(); - - protected abstract void copyTo(SetMultimap other); - - @Override - public boolean containsKey(@NonNull String key) { - return backing().containsKey(sanitizeKey(key)); - } - - @Override - public @NonNull Set getValues(@NonNull String key) { - Collection values = backing().asMap().get(sanitizeKey(key)); - return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of(); - } - - @Override - public boolean contains(@NonNull String key, @NonNull String value) { - return backing().containsEntry(sanitizeKey(key), sanitizeValue(value)); - } - @Override public boolean isSatisfiedBy(@NonNull ContextSet other, @NonNull ContextSatisfyMode mode) { if (this == other) { @@ -96,33 +67,6 @@ public boolean isSatisfiedBy(@NonNull ContextSet other, @NonNull ContextSatisfyM protected abstract boolean otherContainsAll(ContextSet other, ContextSatisfyMode mode); - public abstract Context[] toArray(); - - @Override - public @NonNull Iterator iterator() { - return Iterators.forArray(toArray()); - } - - @Override - public Spliterator spliterator() { - return Arrays.spliterator(toArray()); - } - - @Override - public boolean isEmpty() { - return backing().isEmpty(); - } - - @Override - public int size() { - return backing().size(); - } - - @Override - public int hashCode() { - return backing().hashCode(); - } - static String sanitizeKey(String key) { Objects.requireNonNull(key, "key is null"); if (!Context.isValidKey(key)) { diff --git a/common/src/main/java/me/lucko/luckperms/common/context/contextset/ContextImpl.java b/common/src/main/java/me/lucko/luckperms/common/context/ContextImpl.java similarity index 86% rename from common/src/main/java/me/lucko/luckperms/common/context/contextset/ContextImpl.java rename to common/src/main/java/me/lucko/luckperms/common/context/ContextImpl.java index 4e7c5a13d..0d9b10752 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/contextset/ContextImpl.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/ContextImpl.java @@ -23,13 +23,15 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context.contextset; +package me.lucko.luckperms.common.context; + +import me.lucko.luckperms.common.context.comparator.ContextComparator; import net.luckperms.api.context.Context; import org.checkerframework.checker.nullness.qual.NonNull; -public final class ContextImpl implements Context { +public final class ContextImpl implements Context, Comparable { private final String key; private final String value; @@ -48,6 +50,11 @@ public ContextImpl(String key, String value) { return this.value; } + @Override + public int compareTo(@NonNull Context o) { + return ContextComparator.INSTANCE.compare(this, o); + } + @Override public boolean equals(Object obj) { if (!(obj instanceof Context)) return false; diff --git a/common/src/main/java/me/lucko/luckperms/common/context/contextset/ImmutableContextSetImpl.java b/common/src/main/java/me/lucko/luckperms/common/context/ImmutableContextSetImpl.java similarity index 52% rename from common/src/main/java/me/lucko/luckperms/common/context/contextset/ImmutableContextSetImpl.java rename to common/src/main/java/me/lucko/luckperms/common/context/ImmutableContextSetImpl.java index 862d30ec6..e4b77685d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/contextset/ImmutableContextSetImpl.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/ImmutableContextSetImpl.java @@ -23,16 +23,15 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context.contextset; +package me.lucko.luckperms.common.context; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; -import com.google.common.collect.Multimap; +import com.google.common.collect.Iterators; import com.google.common.collect.Multimaps; -import com.google.common.collect.SetMultimap; -import me.lucko.luckperms.common.context.ContextSetComparator; +import me.lucko.luckperms.common.context.comparator.ContextComparator; import net.luckperms.api.context.Context; import net.luckperms.api.context.ContextSatisfyMode; @@ -44,12 +43,14 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.Spliterator; -public final class ImmutableContextSetImpl extends AbstractContextSet implements ImmutableContextSet { - public static final ImmutableContextSetImpl EMPTY = new ImmutableContextSetImpl(ImmutableSetMultimap.of()); +public final class ImmutableContextSetImpl extends AbstractContextSet implements ImmutableContextSet, ContextSet { + public static final ImmutableContextSetImpl EMPTY = new ImmutableContextSetImpl(new Context[0]); public static ImmutableContextSet of(String key, String value) { key = sanitizeKey(key); @@ -60,35 +61,19 @@ public static ImmutableContextSet of(String key, String value) { return EMPTY; } - return new ImmutableContextSetImpl(ImmutableSetMultimap.of(key, sanitizeValue(value))); + return new ImmutableContextSetImpl(new Context[]{new ContextImpl(key, value)}); } - private final ImmutableSetMultimap map; private final Context[] array; + private final int size; private final int hashCode; - ImmutableContextSetImpl(ImmutableSetMultimap contexts) { - this.map = contexts; - this.hashCode = this.map.hashCode(); + private ImmutableSetMultimap cachedMap; - Set> entries = this.map.entries(); - this.array = new Context[entries.size()]; - int i = 0; - for (Map.Entry e : entries) { - this.array[i++] = new ContextImpl(e.getKey(), e.getValue()); - } - // sort the array at construction so the comparator doesn't need to - Arrays.sort(this.array, ContextSetComparator.CONTEXT_COMPARATOR); - } - - @Override - protected SetMultimap backing() { - return this.map; - } - - @Override - protected void copyTo(SetMultimap other) { - other.putAll(this.map); + ImmutableContextSetImpl(Context[] contexts) { + this.array = contexts; // always sorted + this.size = this.array.length; + this.hashCode = Arrays.hashCode(this.array); } @Override @@ -102,9 +87,20 @@ public boolean isImmutable() { return this; } + public ImmutableSetMultimap toMultimap() { + if (this.cachedMap == null) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + for (Context entry : this.array) { + builder.put(entry.getKey(), entry.getValue()); + } + this.cachedMap = builder.build(); + } + return this.cachedMap; + } + @Override public @NonNull MutableContextSet mutableCopy() { - return new MutableContextSetImpl(this.map); + return new MutableContextSetImpl(toMultimap()); } @Override @@ -114,22 +110,21 @@ public boolean isImmutable() { @Override public @NonNull Map> toMap() { - return Multimaps.asMap(this.map); + return Multimaps.asMap(toMultimap()); } @Deprecated @Override public @NonNull Map toFlattenedMap() { ImmutableMap.Builder m = ImmutableMap.builder(); - for (Map.Entry e : this.map.entries()) { + for (Context e : this.array) { m.put(e.getKey(), e.getValue()); } return m.build(); } - @Override public Context[] toArray() { - return this.array; + return this.array; // only used read-only & internally } @Override @@ -137,8 +132,7 @@ protected boolean otherContainsAll(ContextSet other, ContextSatisfyMode mode) { switch (mode) { // Use other.contains case ALL_VALUES_PER_KEY: { - Set> entries = this.map.entries(); - for (Map.Entry e : entries) { + for (Context e : this.array) { if (!other.contains(e.getKey(), e.getValue())) { return false; } @@ -148,12 +142,26 @@ protected boolean otherContainsAll(ContextSet other, ContextSatisfyMode mode) { // Use other.containsAny case AT_LEAST_ONE_VALUE_PER_KEY: { - Set>> entries = this.map.asMap().entrySet(); - for (Map.Entry> e : entries) { - if (!other.containsAny(e.getKey(), e.getValue())) { - return false; + // exploit the ordered nature to only scan through the array once. + Context[] array = this.array; + for (int i = 0, len = array.length; i < len; i++) { + Context e = array[i]; + + boolean otherContains = other.contains(e.getKey(), e.getValue()); + if (otherContains) { + // skip forward past any other entries with the same key + while (i+1 < len && array[i+1].getKey().equals(e.getKey())) { + i++; + } + } else { + // if this is the last one of the key, return false + int next = i + 1; + if (next >= len || !array[next].getKey().equals(e.getKey())) { + return false; + } } } + return true; } default: @@ -171,21 +179,10 @@ public boolean equals(Object o) { if (that instanceof ImmutableContextSetImpl) { ImmutableContextSetImpl immutableThat = (ImmutableContextSetImpl) that; if (this.hashCode != immutableThat.hashCode) return false; + return Arrays.equals(this.array, immutableThat.array); } - final Multimap thatBacking; - if (that instanceof AbstractContextSet) { - thatBacking = ((AbstractContextSet) that).backing(); - } else { - Map> thatMap = that.toMap(); - ImmutableSetMultimap.Builder thatBuilder = ImmutableSetMultimap.builder(); - for (Map.Entry> e : thatMap.entrySet()) { - thatBuilder.putAll(e.getKey(), e.getValue()); - } - thatBacking = thatBuilder.build(); - } - - return backing().equals(thatBacking); + return this.size() == that.size() && otherContainsAll(that, ContextSatisfyMode.ALL_VALUES_PER_KEY); } @Override @@ -195,57 +192,116 @@ public int hashCode() { @Override public String toString() { - return "ImmutableContextSet(contexts=" + this.map + ")"; + return "ImmutableContextSet(" + Arrays.toString(this.array) + ")"; + } + + @Override + public boolean containsKey(@NonNull String key) { + Objects.requireNonNull(key, "key"); + return Arrays.binarySearch(this.array, new ContextImpl(key, null), ContextComparator.ONLY_KEY) >= 0; + } + + @Override + public @NonNull Set getValues(@NonNull String key) { + Collection values = toMap().get(sanitizeKey(key)); + return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of(); + } + + @Override + public boolean contains(@NonNull Context entry) { + Objects.requireNonNull(entry, "entry"); + return Arrays.binarySearch(this.array, entry) >= 0; + } + + @Override + public boolean contains(@NonNull String key, @NonNull String value) { + Objects.requireNonNull(key, "key"); + Objects.requireNonNull(value, "value"); + return contains(new ContextImpl(key, value)); + } + + @Override + public @NonNull Iterator iterator() { + return Iterators.forArray(this.array); + } + + @Override + public Spliterator spliterator() { + return Arrays.spliterator(this.array); + } + + @Override + public boolean isEmpty() { + return this.size == 0; + } + + @Override + public int size() { + return this.size; } public static final class BuilderImpl implements ImmutableContextSet.Builder { - private ImmutableSetMultimap.Builder builder; + private static final int INITIAL_SIZE = 16; + private Context[] builder = EMPTY.array; + private int size = 0; public BuilderImpl() { } - private synchronized ImmutableSetMultimap.Builder builder() { - if (this.builder == null) { - this.builder = ImmutableSetMultimap.builder(); - } - return this.builder; - } - private void put(String key, String value) { - // special case for server=global and world=global - if (isGlobalServerWorldEntry(key, value)) { + ContextImpl context = new ContextImpl(key, value); + + int pos = Arrays.binarySearch(this.builder, 0, this.size, context); + if (pos >= 0) { return; } - builder().put(key, value); + + int insertPos = -pos - 1; + + Context[] dest; + if (this.builder.length == this.size) { + // grow + dest = new Context[Math.max(this.builder.length * 2, INITIAL_SIZE)]; + System.arraycopy(this.builder, 0, dest, 0, insertPos); + } else { + dest = this.builder; + } + + System.arraycopy(this.builder, insertPos, dest, insertPos + 1, this.size - insertPos); // shift + dest[insertPos] = context; // insert + + this.size++; + this.builder = dest; } @Override public @NonNull BuilderImpl add(@NonNull String key, @NonNull String value) { - put(sanitizeKey(key), sanitizeValue(value)); + key = sanitizeKey(key); + value = sanitizeValue(value); + + // special case for server=global and world=global + if (isGlobalServerWorldEntry(key, value)) { + return this; + } + + put(key, value); return this; } @Override public @NonNull BuilderImpl addAll(@NonNull ContextSet contextSet) { Objects.requireNonNull(contextSet, "contextSet"); - if (contextSet instanceof AbstractContextSet) { - AbstractContextSet other = (AbstractContextSet) contextSet; - if (!other.isEmpty()) { - builder().putAll(other.backing()); - } - } else { - addAll(contextSet.toSet()); - } + addAll(contextSet.toSet()); return this; } @Override public @NonNull ImmutableContextSet build() { - if (this.builder == null) { + if (this.builder.length == 0) { return EMPTY; } else { - return new ImmutableContextSetImpl(this.builder.build()); + return new ImmutableContextSetImpl(Arrays.copyOf(this.builder, this.size)); } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/context/contextset/MutableContextSetImpl.java b/common/src/main/java/me/lucko/luckperms/common/context/MutableContextSetImpl.java similarity index 81% rename from common/src/main/java/me/lucko/luckperms/common/context/contextset/MutableContextSetImpl.java rename to common/src/main/java/me/lucko/luckperms/common/context/MutableContextSetImpl.java index 76a2c86cb..1bb2ffcd2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/contextset/MutableContextSetImpl.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/MutableContextSetImpl.java @@ -23,13 +23,12 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context.contextset; +package me.lucko.luckperms.common.context; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSetMultimap; -import com.google.common.collect.Multimap; +import com.google.common.collect.Iterators; import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; @@ -41,12 +40,15 @@ import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.Spliterator; -public final class MutableContextSetImpl extends AbstractContextSet implements MutableContextSet { +public final class MutableContextSetImpl extends AbstractContextSet implements MutableContextSet, ContextSet { private final SetMultimap map; public MutableContextSetImpl() { @@ -57,18 +59,6 @@ public MutableContextSetImpl() { this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(other)); } - @Override - protected SetMultimap backing() { - return this.map; - } - - @Override - protected void copyTo(SetMultimap other) { - synchronized (this.map) { - other.putAll(this.map); - } - } - @Override public boolean isImmutable() { return false; @@ -80,9 +70,10 @@ public boolean isImmutable() { if (this.map.isEmpty()) { return ImmutableContextSetImpl.EMPTY; } - synchronized (this.map) { - return new ImmutableContextSetImpl(ImmutableSetMultimap.copyOf(this.map)); - } + + Context[] arr = toArray(); + Arrays.sort(arr); + return new ImmutableContextSetImpl(arr); } @Override @@ -129,11 +120,10 @@ public boolean isImmutable() { return builder.build(); } - @Override public Context[] toArray() { - Set> entries = this.map.entries(); Context[] array; synchronized (this.map) { + Set> entries = this.map.entries(); array = new Context[entries.size()]; int i = 0; for (Map.Entry e : entries) { @@ -143,6 +133,42 @@ public Context[] toArray() { return array; } + @Override + public boolean containsKey(@NonNull String key) { + return this.map.containsKey(sanitizeKey(key)); + } + + @Override + public @NonNull Set getValues(@NonNull String key) { + Collection values = this.map.asMap().get(sanitizeKey(key)); + return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of(); + } + + @Override + public boolean contains(@NonNull String key, @NonNull String value) { + return this.map.containsEntry(sanitizeKey(key), sanitizeValue(value)); + } + + @Override + public @NonNull Iterator iterator() { + return Iterators.forArray(toArray()); + } + + @Override + public Spliterator spliterator() { + return Arrays.spliterator(toArray()); + } + + @Override + public boolean isEmpty() { + return this.map.isEmpty(); + } + + @Override + public int size() { + return this.map.size(); + } + @Override public void add(@NonNull String key, @NonNull String value) { key = sanitizeKey(key); @@ -159,12 +185,7 @@ public void add(@NonNull String key, @NonNull String value) { @Override public void addAll(@NonNull ContextSet contextSet) { Objects.requireNonNull(contextSet, "contextSet"); - if (contextSet instanceof AbstractContextSet) { - AbstractContextSet other = (AbstractContextSet) contextSet; - other.copyTo(this.map); - } else { - addAll(contextSet.toSet()); - } + addAll(contextSet.toSet()); } @Override @@ -221,23 +242,16 @@ public boolean equals(Object o) { if (!(o instanceof ContextSet)) return false; final ContextSet that = (ContextSet) o; - final Multimap thatBacking; - if (that instanceof AbstractContextSet) { - thatBacking = ((AbstractContextSet) that).backing(); - } else { - Map> thatMap = that.toMap(); - ImmutableSetMultimap.Builder thatBuilder = ImmutableSetMultimap.builder(); - for (Map.Entry> e : thatMap.entrySet()) { - thatBuilder.putAll(e.getKey(), e.getValue()); - } - thatBacking = thatBuilder.build(); - } + return this.size() == that.size() && otherContainsAll(that, ContextSatisfyMode.ALL_VALUES_PER_KEY); + } - return backing().equals(thatBacking); + @Override + public int hashCode() { + return this.map.hashCode(); } @Override public String toString() { - return "MutableContextSet(contexts=" + this.map + ")"; + return "MutableContextSet(" + this.map + ")"; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/context/ConfigurationContextCalculator.java b/common/src/main/java/me/lucko/luckperms/common/context/calculator/ConfigurationContextCalculator.java similarity index 95% rename from common/src/main/java/me/lucko/luckperms/common/context/ConfigurationContextCalculator.java rename to common/src/main/java/me/lucko/luckperms/common/context/calculator/ConfigurationContextCalculator.java index d499ce499..065896df4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/ConfigurationContextCalculator.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/calculator/ConfigurationContextCalculator.java @@ -23,11 +23,11 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context; +package me.lucko.luckperms.common.context.calculator; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.LuckPermsConfiguration; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import net.luckperms.api.context.ContextConsumer; import net.luckperms.api.context.ContextSet; diff --git a/common/src/main/java/me/lucko/luckperms/common/context/ForwardingContextCalculator.java b/common/src/main/java/me/lucko/luckperms/common/context/calculator/ForwardingContextCalculator.java similarity index 96% rename from common/src/main/java/me/lucko/luckperms/common/context/ForwardingContextCalculator.java rename to common/src/main/java/me/lucko/luckperms/common/context/calculator/ForwardingContextCalculator.java index 0b3a99c0a..187db01ae 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/ForwardingContextCalculator.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/calculator/ForwardingContextCalculator.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context; +package me.lucko.luckperms.common.context.calculator; import net.luckperms.api.context.ContextCalculator; diff --git a/common/src/main/java/me/lucko/luckperms/common/context/WorldNameRewriter.java b/common/src/main/java/me/lucko/luckperms/common/context/calculator/WorldNameRewriter.java similarity index 98% rename from common/src/main/java/me/lucko/luckperms/common/context/WorldNameRewriter.java rename to common/src/main/java/me/lucko/luckperms/common/context/calculator/WorldNameRewriter.java index cc52ca597..98c1123c7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/WorldNameRewriter.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/calculator/WorldNameRewriter.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context; +package me.lucko.luckperms.common.context.calculator; import me.lucko.luckperms.common.config.ConfigKeys; diff --git a/common/src/main/java/me/lucko/luckperms/common/context/comparator/ContextComparator.java b/common/src/main/java/me/lucko/luckperms/common/context/comparator/ContextComparator.java new file mode 100644 index 000000000..ba56de8d1 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/context/comparator/ContextComparator.java @@ -0,0 +1,75 @@ +/* + * 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.context.comparator; + +import net.luckperms.api.context.Context; + +import java.util.Comparator; + +public class ContextComparator implements Comparator { + + public static final ContextComparator INSTANCE = new ContextComparator(false); + + public static final ContextComparator ONLY_KEY = new ContextComparator(true); + + private final boolean onlyKeys; + + public ContextComparator(boolean onlyKeys) { + this.onlyKeys = onlyKeys; + } + + @Override + public int compare(Context o1, Context o2) { + if (o1 == o2) { + return 0; + } + + int i = compareStringsFast(o1.getKey(), o2.getKey()); + if (i != 0) { + return i; + } + + if (this.onlyKeys) { + return 0; + } + + return compareStringsFast(o1.getValue(), o2.getValue()); + } + + public int compare(Context o1, String o2Key, String o2Value) { + int i = compareStringsFast(o1.getKey(), o2Key); + if (i != 0) { + return i; + } + + return compareStringsFast(o1.getValue(), o2Value); + } + + @SuppressWarnings("StringEquality") + private static int compareStringsFast(String o1, String o2) { + return o1 == o2 ? 0 : o1.compareTo(o2); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/context/ContextSetComparator.java b/common/src/main/java/me/lucko/luckperms/common/context/comparator/ContextSetComparator.java similarity index 83% rename from common/src/main/java/me/lucko/luckperms/common/context/ContextSetComparator.java rename to common/src/main/java/me/lucko/luckperms/common/context/comparator/ContextSetComparator.java index 66eb8e132..a91e34724 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/ContextSetComparator.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/comparator/ContextSetComparator.java @@ -23,9 +23,9 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context; +package me.lucko.luckperms.common.context.comparator; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import net.luckperms.api.context.Context; import net.luckperms.api.context.DefaultContextKeys; @@ -90,7 +90,7 @@ public int compare(ImmutableContextSet o1, ImmutableContextSet o2) { Context ent1 = o1Array[i]; Context ent2 = o2Array[i]; - result = compareContexts(ent1, ent2); + result = ContextComparator.INSTANCE.compare(ent1, ent2); if (result != 0) { return result; } @@ -99,29 +99,10 @@ public int compare(ImmutableContextSet o1, ImmutableContextSet o2) { throw new AssertionError("sets are equal? " + o1 + " - " + o2); } - public static final Comparator CONTEXT_COMPARATOR = ContextSetComparator::compareContexts; - private static Context[] toArray(ImmutableContextSet set) { Context[] array = set.toSet().toArray(new Context[0]); - Arrays.sort(array, CONTEXT_COMPARATOR); + Arrays.sort(array, ContextComparator.INSTANCE); return array; } - private static int compareContexts(Context o1, Context o2) { - if (o1 == o2) { - return 0; - } - - int i = compareStringsFast(o1.getKey(), o2.getKey()); - if (i != 0) { - return i; - } - - return compareStringsFast(o1.getValue(), o2.getValue()); - } - - @SuppressWarnings("StringEquality") - private static int compareStringsFast(String o1, String o2) { - return o1 == o2 ? 0 : o1.compareTo(o2); - } } diff --git a/common/src/main/java/me/lucko/luckperms/common/context/ContextManager.java b/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java similarity index 97% rename from common/src/main/java/me/lucko/luckperms/common/context/ContextManager.java rename to common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java index 779763ae2..d5eeffe1f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/ContextManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java @@ -23,11 +23,12 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context; +package me.lucko.luckperms.common.context.manager; import me.lucko.luckperms.common.cache.ExpiringCache; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.calculator.ForwardingContextCalculator; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import net.luckperms.api.context.ContextCalculator; diff --git a/common/src/main/java/me/lucko/luckperms/common/context/QueryOptionsCache.java b/common/src/main/java/me/lucko/luckperms/common/context/manager/QueryOptionsCache.java similarity index 97% rename from common/src/main/java/me/lucko/luckperms/common/context/QueryOptionsCache.java rename to common/src/main/java/me/lucko/luckperms/common/context/manager/QueryOptionsCache.java index 457969b70..15228ce27 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/QueryOptionsCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/manager/QueryOptionsCache.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context; +package me.lucko.luckperms.common.context.manager; import me.lucko.luckperms.common.cache.ExpiringCache; diff --git a/common/src/main/java/me/lucko/luckperms/common/context/QueryOptionsSupplier.java b/common/src/main/java/me/lucko/luckperms/common/context/manager/QueryOptionsSupplier.java similarity index 96% rename from common/src/main/java/me/lucko/luckperms/common/context/QueryOptionsSupplier.java rename to common/src/main/java/me/lucko/luckperms/common/context/manager/QueryOptionsSupplier.java index 351fb25f1..7e5af3acc 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/QueryOptionsSupplier.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/manager/QueryOptionsSupplier.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context; +package me.lucko.luckperms.common.context.manager; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.query.QueryOptions; diff --git a/common/src/main/java/me/lucko/luckperms/common/context/ContextSetConfigurateSerializer.java b/common/src/main/java/me/lucko/luckperms/common/context/serializer/ContextSetConfigurateSerializer.java similarity index 94% rename from common/src/main/java/me/lucko/luckperms/common/context/ContextSetConfigurateSerializer.java rename to common/src/main/java/me/lucko/luckperms/common/context/serializer/ContextSetConfigurateSerializer.java index dffaf87b5..f9b5bbada 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/ContextSetConfigurateSerializer.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/serializer/ContextSetConfigurateSerializer.java @@ -23,12 +23,12 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context; +package me.lucko.luckperms.common.context.serializer; import com.google.common.base.Preconditions; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; -import me.lucko.luckperms.common.context.contextset.MutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.MutableContextSetImpl; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.MutableContextSet; diff --git a/common/src/main/java/me/lucko/luckperms/common/context/ContextSetJsonSerializer.java b/common/src/main/java/me/lucko/luckperms/common/context/serializer/ContextSetJsonSerializer.java similarity index 95% rename from common/src/main/java/me/lucko/luckperms/common/context/ContextSetJsonSerializer.java rename to common/src/main/java/me/lucko/luckperms/common/context/serializer/ContextSetJsonSerializer.java index bcee83d31..70b4b83b5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/ContextSetJsonSerializer.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/serializer/ContextSetJsonSerializer.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.context; +package me.lucko.luckperms.common.context.serializer; import com.google.common.base.Preconditions; import com.google.gson.Gson; @@ -32,8 +32,8 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; -import me.lucko.luckperms.common.context.contextset.MutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.MutableContextSetImpl; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.MutableContextSet; diff --git a/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java b/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java index c25c76798..53a647f34 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java @@ -26,7 +26,7 @@ package me.lucko.luckperms.common.model.manager.user; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.manager.AbstractManager; import me.lucko.luckperms.common.model.manager.group.GroupManager; diff --git a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java index 9b500c15f..8ec1d828e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java @@ -26,7 +26,7 @@ package me.lucko.luckperms.common.model.nodemap; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.ContextSetComparator; +import me.lucko.luckperms.common.context.comparator.ContextSetComparator; import me.lucko.luckperms.common.model.InheritanceOrigin; import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.nodemap.MutateResult.ChangeType; diff --git a/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java b/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java index 00fde5c4d..8840dcde0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.common.node; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.ImmutableContextSet; diff --git a/common/src/main/java/me/lucko/luckperms/common/node/comparator/NodeWithContextComparator.java b/common/src/main/java/me/lucko/luckperms/common/node/comparator/NodeWithContextComparator.java index 2b84a53c5..16f405857 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/comparator/NodeWithContextComparator.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/comparator/NodeWithContextComparator.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.common.node.comparator; -import me.lucko.luckperms.common.context.ContextSetComparator; +import me.lucko.luckperms.common.context.comparator.ContextSetComparator; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.node.Node; diff --git a/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java b/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java index a75afada9..138bc55ea 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java @@ -29,7 +29,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import me.lucko.luckperms.common.context.ContextSetJsonSerializer; +import me.lucko.luckperms.common.context.serializer.ContextSetJsonSerializer; import me.lucko.luckperms.common.node.factory.NodeBuilders; import net.luckperms.api.node.Node; diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index aa1eedca3..f058a478d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -32,7 +32,7 @@ import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.LuckPermsConfiguration; import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter; -import me.lucko.luckperms.common.context.ConfigurationContextCalculator; +import me.lucko.luckperms.common.context.calculator.ConfigurationContextCalculator; import me.lucko.luckperms.common.dependencies.Dependency; import me.lucko.luckperms.common.dependencies.DependencyManager; import me.lucko.luckperms.common.event.AbstractEventBus; diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java index d002d2ece..25fc4b544 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java @@ -31,7 +31,7 @@ import me.lucko.luckperms.common.command.CommandManager; import me.lucko.luckperms.common.command.abstraction.Command; import me.lucko.luckperms.common.config.LuckPermsConfiguration; -import me.lucko.luckperms.common.context.ContextManager; +import me.lucko.luckperms.common.context.manager.ContextManager; import me.lucko.luckperms.common.dependencies.DependencyManager; import me.lucko.luckperms.common.event.EventDispatcher; import me.lucko.luckperms.common.extension.SimpleExtensionManager; diff --git a/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsBuilderImpl.java b/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsBuilderImpl.java index 539a97432..868bc5c50 100644 --- a/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsBuilderImpl.java +++ b/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsBuilderImpl.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.common.query; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.ImmutableContextSet; diff --git a/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsImpl.java b/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsImpl.java index 7eab66e99..ef93f4fe1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsImpl.java +++ b/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsImpl.java @@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import net.luckperms.api.context.ContextSatisfyMode; import net.luckperms.api.context.ContextSet; diff --git a/common/src/main/java/me/lucko/luckperms/common/sender/Sender.java b/common/src/main/java/me/lucko/luckperms/common/sender/Sender.java index 92d177e15..a4287a7d0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/sender/Sender.java +++ b/common/src/main/java/me/lucko/luckperms/common/sender/Sender.java @@ -26,7 +26,7 @@ package me.lucko.luckperms.common.sender; import me.lucko.luckperms.common.command.access.CommandPermission; -import me.lucko.luckperms.common.context.ContextManager; +import me.lucko.luckperms.common.context.manager.ContextManager; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import net.kyori.adventure.text.Component; diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java index e812e83ad..908dab15c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java @@ -29,8 +29,8 @@ import me.lucko.luckperms.common.actionlog.Log; import me.lucko.luckperms.common.bulkupdate.BulkUpdate; -import me.lucko.luckperms.common.context.ContextSetConfigurateSerializer; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.serializer.ContextSetConfigurateSerializer; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.HolderType; import me.lucko.luckperms.common.model.Track; diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java index 79e9bb0eb..3375ec411 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java @@ -40,7 +40,7 @@ import me.lucko.luckperms.common.actionlog.Log; import me.lucko.luckperms.common.actionlog.LoggedAction; import me.lucko.luckperms.common.bulkupdate.BulkUpdate; -import me.lucko.luckperms.common.context.contextset.MutableContextSetImpl; +import me.lucko.luckperms.common.context.MutableContextSetImpl; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.HolderType; diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index d1a4e60db..aab52eb1b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -34,7 +34,7 @@ import me.lucko.luckperms.common.bulkupdate.BulkUpdate; import me.lucko.luckperms.common.bulkupdate.BulkUpdateStatistics; import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder; -import me.lucko.luckperms.common.context.ContextSetJsonSerializer; +import me.lucko.luckperms.common.context.serializer.ContextSetJsonSerializer; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.User; diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java index ca286e34a..868303c57 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java @@ -29,8 +29,8 @@ import com.google.gson.JsonObject; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.ContextSetJsonSerializer; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.serializer.ContextSetJsonSerializer; import me.lucko.luckperms.common.http.AbstractHttpClient; import me.lucko.luckperms.common.http.UnsuccessfulRequestException; import me.lucko.luckperms.common.locale.Message; diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java index 54af73721..6791f56a1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java @@ -33,7 +33,7 @@ import me.lucko.luckperms.common.command.access.ArgumentPermissions; import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.command.utils.StorageAssistant; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; diff --git a/common/src/test/java/me/lucko/luckperms/common/context/ContextSetTest.java b/common/src/test/java/me/lucko/luckperms/common/context/ContextSetTest.java new file mode 100644 index 000000000..94079867c --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/context/ContextSetTest.java @@ -0,0 +1,158 @@ +/* + * 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.context; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import net.luckperms.api.context.Context; +import net.luckperms.api.context.ContextSatisfyMode; +import net.luckperms.api.context.ImmutableContextSet; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ContextSetTest { + + @Test + public void testImmutableBuilder() { + List> tests = ImmutableList.of( + builder -> { + builder.add("test", "a"); + builder.add("test", "b"); + builder.add("test", "c"); + }, + builder -> { + builder.add("test", "c"); + builder.add("test", "b"); + builder.add("test", "a"); + }, + builder -> { + builder.add("test", "b"); + builder.add("test", "a"); + builder.add("test", "c"); + }, + builder -> { + builder.add("test", "b"); + builder.add("test", "c"); + builder.add("test", "a"); + }, + builder -> { + builder.add("test", "a"); + builder.add("test", "a"); + builder.add("test", "b"); + builder.add("test", "c"); + } + ); + + for (Consumer action : tests) { + ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl(); + action.accept(builder); + ImmutableContextSet set = builder.build(); + + ImmutableSet expected = ImmutableSet.of( + new ContextImpl("test", "a"), + new ContextImpl("test", "b"), + new ContextImpl("test", "c") + ); + + assertEquals(expected, set.toSet()); + assertEquals(3, set.size()); + + assertTrue(set.contains("test", "a")); + assertTrue(set.contains("test", "b")); + assertTrue(set.contains("test", "c")); + } + } + + @Test + public void testImmutableContains() { + ImmutableContextSet set = new ImmutableContextSetImpl.BuilderImpl() + .add("test", "a") + .add("test", "a") + .add("test", "b") + .add("test", "c") + .build(); + + assertTrue(set.contains("test", "a")); + assertFalse(set.contains("test", "z")); + assertFalse(set.contains("aaa", "a")); + + assertTrue(set.containsKey("test")); + assertFalse(set.containsKey("aaa")); + } + + @Test + public void testImmutableContainsAll() { + ImmutableContextSetImpl set = (ImmutableContextSetImpl) new ImmutableContextSetImpl.BuilderImpl() + .add("test", "a") + .add("test", "b") + .add("test", "c") + .add("other", "a") + .add("other", "b") + .build(); + + List> trueTests = ImmutableList.of( + builder -> builder.add("test", "a").add("other", "a"), + builder -> builder.add("test", "b").add("other", "a"), + builder -> builder.add("test", "c").add("other", "a"), + builder -> builder.add("test", "c").add("other", "b") + ); + + for (Consumer test : trueTests) { + ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl(); + test.accept(builder); + assertTrue(set.otherContainsAll( + builder.build(), + ContextSatisfyMode.AT_LEAST_ONE_VALUE_PER_KEY) + ); + } + + List> falseTests = ImmutableList.of( + builder -> builder.add("test", "a").add("other", "z"), + builder -> builder.add("test", "b").add("other", "z"), + builder -> builder.add("test", "b"), + builder -> builder.add("test", "c"), + builder -> {} + ); + + for (Consumer test : falseTests) { + ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl(); + test.accept(builder); + assertFalse(set.otherContainsAll( + builder.build(), + ContextSatisfyMode.AT_LEAST_ONE_VALUE_PER_KEY) + ); + } + } + +} diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricContextManager.java b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricContextManager.java index eac3eddfc..39498db5b 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricContextManager.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricContextManager.java @@ -26,8 +26,8 @@ package me.lucko.luckperms.fabric.context; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.ContextManager; -import me.lucko.luckperms.common.context.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.ContextManager; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.fabric.model.MixinUser; diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java index 7214d071b..193e73e3c 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java @@ -26,7 +26,7 @@ package me.lucko.luckperms.fabric.context; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.util.EnumNamer; import me.lucko.luckperms.fabric.LPFabricPlugin; import me.lucko.luckperms.fabric.event.PlayerChangeWorldCallback; diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java index d7b5c3a21..cc30d1e9c 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java @@ -26,7 +26,7 @@ package me.lucko.luckperms.fabric.mixin; import me.lucko.luckperms.common.cacheddata.type.PermissionCache; -import me.lucko.luckperms.common.context.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.locale.TranslationManager; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/model/MixinUser.java b/fabric/src/main/java/me/lucko/luckperms/fabric/model/MixinUser.java index 4215908d6..2a6911615 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/model/MixinUser.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/model/MixinUser.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.fabric.model; -import me.lucko.luckperms.common.context.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.fabric.context.FabricContextManager; 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 9514862af..4cacd3c5c 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 @@ -29,8 +29,8 @@ import me.lucko.luckperms.common.cache.LoadingMap; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.ContextManager; -import me.lucko.luckperms.common.context.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.ContextManager; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.util.CaffeineFactory; import me.lucko.luckperms.nukkit.LPNukkitPlugin; diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitPlayerCalculator.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitPlayerCalculator.java index a31fab60b..82df303b5 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitPlayerCalculator.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/context/NukkitPlayerCalculator.java @@ -26,7 +26,7 @@ package me.lucko.luckperms.nukkit.context; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.nukkit.LPNukkitPlugin; import net.luckperms.api.context.Context; diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissible.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissible.java index 05b934770..b7296a4b6 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissible.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissible.java @@ -30,7 +30,7 @@ import me.lucko.luckperms.common.calculator.result.TristateResult; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; import me.lucko.luckperms.nukkit.LPNukkitPlugin; diff --git a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/DescriptionBuilder.java b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/DescriptionBuilder.java index f1c4c8bf3..f6d3bd583 100644 --- a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/DescriptionBuilder.java +++ b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/DescriptionBuilder.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.sponge.service.proxy.api6; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubject; diff --git a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java index 1457784cb..237362660 100644 --- a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java +++ b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.sponge.service.proxy.api6; -import me.lucko.luckperms.common.context.QueryOptionsSupplier; +import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.common.util.ImmutableCollectors; import me.lucko.luckperms.sponge.service.CompatibilityUtil; import me.lucko.luckperms.sponge.service.model.LPPermissionService; diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/DescriptionBuilder.java b/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/DescriptionBuilder.java index 40b50a730..cccb156f9 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/DescriptionBuilder.java +++ b/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/DescriptionBuilder.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.sponge.service.proxy.api7; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubject; diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java b/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java index 91d30e85d..e8492738c 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java +++ b/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.sponge.service.proxy.api7; -import me.lucko.luckperms.common.context.QueryOptionsSupplier; +import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.sponge.service.CompatibilityUtil; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubject; diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/CompatibilityUtil.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/CompatibilityUtil.java index 8eabb3b26..7cd3111d6 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/CompatibilityUtil.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/CompatibilityUtil.java @@ -27,8 +27,8 @@ import com.google.common.collect.ImmutableSet; -import me.lucko.luckperms.common.context.contextset.ContextImpl; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ContextImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.sponge.service.context.ForwardingContextSet; import me.lucko.luckperms.sponge.service.context.ForwardingImmutableContextSet; diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java index 74ea0d264..8b4159bf6 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java @@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMap; -import me.lucko.luckperms.common.context.ContextManager; +import me.lucko.luckperms.common.context.manager.ContextManager; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.sponge.service.reference.SubjectReferenceFactory; 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 90b415837..25b47dca2 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 @@ -27,9 +27,9 @@ import com.github.benmanes.caffeine.cache.LoadingCache; -import me.lucko.luckperms.common.context.ContextManager; -import me.lucko.luckperms.common.context.QueryOptionsCache; -import me.lucko.luckperms.common.context.QueryOptionsSupplier; +import me.lucko.luckperms.common.context.manager.ContextManager; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.common.util.CaffeineFactory; import me.lucko.luckperms.sponge.LPSpongePlugin; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java b/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java index c2103b057..92af5d750 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java @@ -26,7 +26,7 @@ package me.lucko.luckperms.sponge.context; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.sponge.LPSpongePlugin; import net.luckperms.api.context.Context; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java index aababb710..735ebe38f 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java @@ -32,7 +32,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.model.manager.group.AbstractGroupManager; import me.lucko.luckperms.common.node.matcher.StandardNodeMatchers; import me.lucko.luckperms.common.storage.misc.DataConstraints; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeUserManager.java index fe200f929..330dd9913 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeUserManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeUserManager.java @@ -32,7 +32,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.model.manager.user.AbstractUserManager; import me.lucko.luckperms.common.model.manager.user.UserHousekeeper; import me.lucko.luckperms.common.node.matcher.StandardNodeMatchers; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java index 9d9590ca2..8e87e41bf 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java @@ -29,7 +29,7 @@ import com.google.common.collect.ImmutableSet; import me.lucko.luckperms.common.cache.LoadingMap; -import me.lucko.luckperms.common.context.ContextManager; +import me.lucko.luckperms.common.context.manager.ContextManager; import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.model.manager.SpongeGroupManager; 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 cb43a76f2..8deb8f9e3 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 @@ -25,7 +25,7 @@ package me.lucko.luckperms.sponge.service.model; -import me.lucko.luckperms.common.context.ForwardingContextCalculator; +import me.lucko.luckperms.common.context.calculator.ForwardingContextCalculator; import net.luckperms.api.context.ContextConsumer; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java index 8c1ab6dfb..74e6b881c 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java @@ -30,7 +30,7 @@ import com.google.common.collect.ImmutableSet; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.ContextSetComparator; +import me.lucko.luckperms.common.context.comparator.ContextSetComparator; import me.lucko.luckperms.sponge.service.ProxyFactory; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubject; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java index 9aad7faf1..c88252895 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java @@ -31,7 +31,7 @@ import com.google.common.collect.ImmutableSet; import me.lucko.luckperms.common.cache.LoadingMap; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.util.ImmutableCollectors; import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.sponge.service.LuckPermsService; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectDataContainer.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectDataContainer.java index a3efda34e..a26626aac 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectDataContainer.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectDataContainer.java @@ -32,8 +32,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import me.lucko.luckperms.common.context.ContextSetComparator; -import me.lucko.luckperms.common.context.ContextSetJsonSerializer; +import me.lucko.luckperms.common.context.comparator.ContextSetComparator; +import me.lucko.luckperms.common.context.serializer.ContextSetJsonSerializer; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubjectData; import me.lucko.luckperms.sponge.service.model.LPSubjectReference; 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 ee76620ff..33ed407b0 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 @@ -28,9 +28,9 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import com.velocitypowered.api.proxy.Player; -import me.lucko.luckperms.common.context.ContextManager; -import me.lucko.luckperms.common.context.QueryOptionsCache; -import me.lucko.luckperms.common.context.QueryOptionsSupplier; +import me.lucko.luckperms.common.context.manager.ContextManager; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.common.util.CaffeineFactory; import me.lucko.luckperms.velocity.LPVelocityPlugin; diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java b/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java index 7153e8c71..1287a166f 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java @@ -33,7 +33,7 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.velocity.LPVelocityPlugin; import net.luckperms.api.context.ContextCalculator; diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/service/PlayerPermissionProvider.java b/velocity/src/main/java/me/lucko/luckperms/velocity/service/PlayerPermissionProvider.java index a3ca824cd..5287f04db 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/service/PlayerPermissionProvider.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/service/PlayerPermissionProvider.java @@ -32,7 +32,7 @@ import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; -import me.lucko.luckperms.common.context.QueryOptionsSupplier; +import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; From 272d289d4a64f96e5d720ff20ec69e54035a3d3f Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 18 Nov 2021 18:21:30 +0000 Subject: [PATCH 038/314] More context performance improvements (#3209) --- .../vault/LuckPermsVaultPermission.java | 21 ++++++++++++------- .../luckperms/common/context/ContextImpl.java | 6 ++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java index 448ff038a..345fda82d 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java @@ -47,6 +47,7 @@ import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.DefaultContextKeys; import net.luckperms.api.context.MutableContextSet; import net.luckperms.api.model.data.DataType; @@ -332,13 +333,13 @@ private String rewriteGroupName(String name) { // utility method for getting a contexts instance for a given vault lookup. QueryOptions getQueryOptions(@Nullable UUID uuid, @Nullable String world) { - MutableContextSet context; + ContextSet context; Player player = Optional.ofNullable(uuid).flatMap(u -> this.plugin.getBootstrap().getPlayer(u)).orElse(null); if (player != null) { - context = this.plugin.getContextManager().getContext(player).mutableCopy(); + context = this.plugin.getContextManager().getContext(player); } else { - context = this.plugin.getContextManager().getStaticContext().mutableCopy(); + context = this.plugin.getContextManager().getStaticContext(); } String playerWorld = player == null ? null : player.getWorld().getName(); @@ -346,20 +347,26 @@ QueryOptions getQueryOptions(@Nullable UUID uuid, @Nullable String world) { // if world is null, we want to do a lookup in the players current context // if world is not null, we want to do a lookup in that specific world if (world != null && !world.isEmpty() && !world.equalsIgnoreCase(playerWorld)) { + MutableContextSet mutContext = context.mutableCopy(); + context = mutContext; + // remove already accumulated worlds - context.removeAll(DefaultContextKeys.WORLD_KEY); + mutContext.removeAll(DefaultContextKeys.WORLD_KEY); // add the vault world - context.add(DefaultContextKeys.WORLD_KEY, world.toLowerCase(Locale.ROOT)); + mutContext.add(DefaultContextKeys.WORLD_KEY, world.toLowerCase(Locale.ROOT)); } // if we're using a special vault server if (useVaultServer()) { + MutableContextSet mutContext = context instanceof MutableContextSet ? (MutableContextSet) context : context.mutableCopy(); + context = mutContext; + // remove the normal server context from the set - context.remove(DefaultContextKeys.SERVER_KEY, getServer()); + mutContext.remove(DefaultContextKeys.SERVER_KEY, getServer()); // add the vault specific server if (!getVaultServer().equals("global")) { - context.add(DefaultContextKeys.SERVER_KEY, getVaultServer()); + mutContext.add(DefaultContextKeys.SERVER_KEY, getVaultServer()); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/context/ContextImpl.java b/common/src/main/java/me/lucko/luckperms/common/context/ContextImpl.java index 0d9b10752..2aaf761d1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/ContextImpl.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/ContextImpl.java @@ -57,6 +57,7 @@ public int compareTo(@NonNull Context o) { @Override public boolean equals(Object obj) { + if (this == obj) return true; if (!(obj instanceof Context)) return false; Context that = (Context) obj; return this.key.equals(that.getKey()) && this.value.equals(that.getValue()); @@ -66,4 +67,9 @@ public boolean equals(Object obj) { public int hashCode() { return this.key.hashCode() ^ this.value.hashCode(); } + + @Override + public String toString() { + return this.key + '=' + this.value; + } } From 9204848ffb2c162712bbe69412ef01df18e8ea80 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 27 Nov 2021 09:43:47 +0000 Subject: [PATCH 039/314] Fix username lookup returning "null" instead of null (#3220) --- .../common/storage/implementation/mongodb/MongoStorage.java | 5 ++++- .../common/storage/implementation/sql/SqlStorage.java | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java index 3375ec411..bf595a97a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java @@ -573,7 +573,10 @@ public String getPlayerName(UUID uniqueId) { MongoCollection c = this.database.getCollection(this.prefix + "uuid"); Document doc = c.find(new Document("_id", uniqueId)).first(); if (doc != null) { - return doc.get("name", String.class); + String username = doc.get("name", String.class); + if (username != null && !username.equals("null")) { + return username; + } } return null; } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index aab52eb1b..f1170da0e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -694,7 +694,10 @@ public String getPlayerName(UUID uniqueId) throws SQLException { ps.setString(1, uniqueId.toString()); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { - return rs.getString("username"); + String username = rs.getString("username"); + if (username != null && !username.equals("null")) { + return username; + } } } } From 594f50bee756e15f5d1e51c0896f5d01a19ad20f Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 27 Nov 2021 09:49:01 +0000 Subject: [PATCH 040/314] Add config option to disable context calculators --- bukkit/src/main/resources/config.yml | 7 +++++ bungee/src/main/resources/config.yml | 7 +++++ .../tabcomplete/CompletionSupplier.java | 6 +++-- .../command/tabcomplete/TabCompleter.java | 20 -------------- .../command/tabcomplete/TabCompletions.java | 5 ++-- .../luckperms/common/config/ConfigKeys.java | 12 +++++++++ .../context/manager/ContextManager.java | 12 +++++++++ .../luckperms/common/util/Predicates.java | 19 ++++++++++++++ .../common/context/ContextSetTest.java | 26 +++++++++---------- fabric/src/main/resources/luckperms.conf | 7 +++++ nukkit/src/main/resources/config.yml | 7 +++++ sponge/src/main/resources/luckperms.conf | 7 +++++ velocity/src/main/resources/config.yml | 7 +++++ 13 files changed, 105 insertions(+), 37 deletions(-) diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index a4dd153e7..a31dc3060 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -551,6 +551,13 @@ apply-bukkit-attachment-permissions: true # | Extra settings | # # +----------------------------------------------------------------------------------------------+ # +# A list of context calculators which will be skipped when calculating contexts. +# +# - You can disable context calculators by either: +# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator) +# => specifying a sub-section of the Java package used by the calculator (e.g. com.example) +disabled-context-calculators: [] + # Allows you to set "aliases" for the worlds sent forward for context calculation. # # - These aliases are provided in addition to the real world name. Applied recursively. diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index a3b5a19f7..53a2bd837 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -531,6 +531,13 @@ apply-bungee-config-permissions: false # | Extra settings | # # +----------------------------------------------------------------------------------------------+ # +# A list of context calculators which will be skipped when calculating contexts. +# +# - You can disable context calculators by either: +# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator) +# => specifying a sub-section of the Java package used by the calculator (e.g. com.example) +disabled-context-calculators: [] + # Allows you to set "aliases" for the worlds sent forward for context calculation. # # - These aliases are provided in addition to the real world name. Applied recursively. diff --git a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/CompletionSupplier.java b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/CompletionSupplier.java index b4f5f3a9a..f755e74c2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/CompletionSupplier.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/CompletionSupplier.java @@ -25,6 +25,8 @@ package me.lucko.luckperms.common.command.tabcomplete; +import me.lucko.luckperms.common.util.Predicates; + import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -46,7 +48,7 @@ static CompletionSupplier startsWith(Collection strings) { } static CompletionSupplier startsWith(Supplier> stringsSupplier) { - return partial -> stringsSupplier.get().filter(TabCompleter.startsWithIgnoreCase(partial)).collect(Collectors.toList()); + return partial -> stringsSupplier.get().filter(Predicates.startsWithIgnoreCase(partial)).collect(Collectors.toList()); } static CompletionSupplier contains(String... strings) { @@ -58,7 +60,7 @@ static CompletionSupplier contains(Collection strings) { } static CompletionSupplier contains(Supplier> stringsSupplier) { - return partial -> stringsSupplier.get().filter(TabCompleter.containsIgnoreCase(partial)).collect(Collectors.toList()); + return partial -> stringsSupplier.get().filter(Predicates.containsIgnoreCase(partial)).collect(Collectors.toList()); } List supplyCompletions(String partial); diff --git a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompleter.java b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompleter.java index da985c438..4506023d6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompleter.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompleter.java @@ -29,9 +29,7 @@ import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.function.Predicate; /** * Utility for computing tab completion results @@ -99,22 +97,4 @@ private List getCompletions(int position, String partial) { return this.suppliers.getOrDefault(position, CompletionSupplier.EMPTY).supplyCompletions(partial); } - static Predicate startsWithIgnoreCase(String prefix) { - return string -> { - if (string.length() < prefix.length()) { - return false; - } - return string.regionMatches(true, 0, prefix, 0, prefix.length()); - }; - } - - static Predicate containsIgnoreCase(String substring) { - return string -> { - if (string.length() < substring.length()) { - return false; - } - return string.toLowerCase(Locale.ROOT).contains(substring.toLowerCase(Locale.ROOT)); - }; - } - } diff --git a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompletions.java b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompletions.java index e87c7fc13..53dc22693 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompletions.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/tabcomplete/TabCompletions.java @@ -30,6 +30,7 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.treeview.TreeNode; +import me.lucko.luckperms.common.util.Predicates; import net.luckperms.api.context.ImmutableContextSet; @@ -90,7 +91,7 @@ public TabCompletions(LuckPermsPlugin plugin) { } return root.getChildren().get().keySet().stream() - .filter(TabCompleter.startsWithIgnoreCase(incomplete)) + .filter(Predicates.startsWithIgnoreCase(incomplete)) .map(s -> String.join(".", parts) + "." + s) .collect(Collectors.toList()); }; @@ -112,7 +113,7 @@ public TabCompletions(LuckPermsPlugin plugin) { String value = partial.substring(index + 1).trim(); Set potentialValues = potentialContexts.getValues(key); return potentialValues.stream() - .filter(TabCompleter.startsWithIgnoreCase(value)) + .filter(Predicates.startsWithIgnoreCase(value)) .map(s -> key + "=" + s) .collect(Collectors.toList()); }; diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 7bd64e662..11eee3e89 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -44,6 +44,7 @@ import me.lucko.luckperms.common.storage.implementation.split.SplitStorageType; import me.lucko.luckperms.common.storage.misc.StorageCredentials; import me.lucko.luckperms.common.util.ImmutableCollectors; +import me.lucko.luckperms.common.util.Predicates; import net.luckperms.api.context.ContextSatisfyMode; import net.luckperms.api.metastacking.DuplicateRemovalFunction; @@ -64,6 +65,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -515,6 +517,16 @@ private ConfigKeys() {} */ public static final ConfigKey FABRIC_INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS = booleanKey("integrated-server-owner-bypasses-checks", true); + /** + * Disabled context calculators + */ + public static final ConfigKey>> DISABLED_CONTEXT_CALCULATORS = key(c -> { + return c.getStringList("disabled-context-calculators", ImmutableList.of()) + .stream() + .map(Predicates::startsWithIgnoreCase) + .collect(ImmutableCollectors.toSet()); + }); + /** * The world rewrites map */ diff --git a/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java b/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java index d5eeffe1f..4c82dcdd8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java @@ -42,8 +42,10 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; /** * Base implementation of {@link ContextManager} which caches content lookups. @@ -116,6 +118,16 @@ public void signalContextUpdate(S subject) { protected abstract void invalidateCache(S subject); public void registerCalculator(ContextCalculator calculator) { + String calculatorClass = calculator.getClass().getName(); + + Set> disabledCalculators = this.plugin.getConfiguration().get(ConfigKeys.DISABLED_CONTEXT_CALCULATORS); + for (Predicate disabledPattern : disabledCalculators) { + if (disabledPattern.test(calculatorClass)) { + this.plugin.getLogger().info("Ignoring registration of disabled context calculator: " + calculatorClass); + return; + } + } + this.calculators.add(calculator); } diff --git a/common/src/main/java/me/lucko/luckperms/common/util/Predicates.java b/common/src/main/java/me/lucko/luckperms/common/util/Predicates.java index 76fdb0137..1fd028861 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/Predicates.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/Predicates.java @@ -29,6 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Locale; import java.util.function.Predicate; /** @@ -86,4 +87,22 @@ public static Predicate is(T t) { return t::equals; } + public static Predicate startsWithIgnoreCase(String prefix) { + return string -> { + if (string.length() < prefix.length()) { + return false; + } + return string.regionMatches(true, 0, prefix, 0, prefix.length()); + }; + } + + public static Predicate containsIgnoreCase(String substring) { + return string -> { + if (string.length() < substring.length()) { + return false; + } + return string.toLowerCase(Locale.ROOT).contains(substring.toLowerCase(Locale.ROOT)); + }; + } + } diff --git a/common/src/test/java/me/lucko/luckperms/common/context/ContextSetTest.java b/common/src/test/java/me/lucko/luckperms/common/context/ContextSetTest.java index 94079867c..d23dfd21b 100644 --- a/common/src/test/java/me/lucko/luckperms/common/context/ContextSetTest.java +++ b/common/src/test/java/me/lucko/luckperms/common/context/ContextSetTest.java @@ -114,18 +114,18 @@ public void testImmutableContains() { @Test public void testImmutableContainsAll() { ImmutableContextSetImpl set = (ImmutableContextSetImpl) new ImmutableContextSetImpl.BuilderImpl() - .add("test", "a") - .add("test", "b") - .add("test", "c") - .add("other", "a") - .add("other", "b") + .add("aaa", "a") + .add("aaa", "b") + .add("aaa", "c") + .add("bbb", "a") + .add("bbb", "b") .build(); List> trueTests = ImmutableList.of( - builder -> builder.add("test", "a").add("other", "a"), - builder -> builder.add("test", "b").add("other", "a"), - builder -> builder.add("test", "c").add("other", "a"), - builder -> builder.add("test", "c").add("other", "b") + builder -> builder.add("aaa", "a").add("bbb", "a"), + builder -> builder.add("aaa", "b").add("bbb", "a"), + builder -> builder.add("aaa", "c").add("bbb", "a"), + builder -> builder.add("aaa", "c").add("bbb", "b") ); for (Consumer test : trueTests) { @@ -138,10 +138,10 @@ public void testImmutableContainsAll() { } List> falseTests = ImmutableList.of( - builder -> builder.add("test", "a").add("other", "z"), - builder -> builder.add("test", "b").add("other", "z"), - builder -> builder.add("test", "b"), - builder -> builder.add("test", "c"), + builder -> builder.add("aaa", "a").add("bbb", "z"), + builder -> builder.add("aaa", "b").add("bbb", "z"), + builder -> builder.add("aaa", "b"), + builder -> builder.add("aaa", "c"), builder -> {} ); diff --git a/fabric/src/main/resources/luckperms.conf b/fabric/src/main/resources/luckperms.conf index 8d3686dc8..f875e3e5a 100644 --- a/fabric/src/main/resources/luckperms.conf +++ b/fabric/src/main/resources/luckperms.conf @@ -537,6 +537,13 @@ integrated-server-owner-bypasses-checks = true # | Extra settings | # # +----------------------------------------------------------------------------------------------+ # +# A list of context calculators which will be skipped when calculating contexts. +# +# - You can disable context calculators by either: +# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator) +# => specifying a sub-section of the Java package used by the calculator (e.g. com.example) +disabled-context-calculators = [] + # Allows you to set "aliases" for the worlds sent forward for context calculation. # # - These aliases are provided in addition to the real world name. Applied recursively. diff --git a/nukkit/src/main/resources/config.yml b/nukkit/src/main/resources/config.yml index e6cb4d832..6d9e2d068 100644 --- a/nukkit/src/main/resources/config.yml +++ b/nukkit/src/main/resources/config.yml @@ -546,6 +546,13 @@ apply-nukkit-attachment-permissions: true # | Extra settings | # # +----------------------------------------------------------------------------------------------+ # +# A list of context calculators which will be skipped when calculating contexts. +# +# - You can disable context calculators by either: +# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator) +# => specifying a sub-section of the Java package used by the calculator (e.g. com.example) +disabled-context-calculators: [] + # Allows you to set "aliases" for the worlds sent forward for context calculation. # # - These aliases are provided in addition to the real world name. Applied recursively. diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index ae22acfd5..0f58a2775 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -547,6 +547,13 @@ apply-sponge-default-subjects=true # | Extra settings | # # +----------------------------------------------------------------------------------------------+ # +# A list of context calculators which will be skipped when calculating contexts. +# +# - You can disable context calculators by either: +# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator) +# => specifying a sub-section of the Java package used by the calculator (e.g. com.example) +disabled-context-calculators = [] + # Allows you to set "aliases" for the worlds sent forward for context calculation. # # - These aliases are provided in addition to the real world name. Applied recursively. diff --git a/velocity/src/main/resources/config.yml b/velocity/src/main/resources/config.yml index 7f82c0e09..7321eabf5 100644 --- a/velocity/src/main/resources/config.yml +++ b/velocity/src/main/resources/config.yml @@ -517,6 +517,13 @@ apply-shorthand: true # | Extra settings | # # +----------------------------------------------------------------------------------------------+ # +# A list of context calculators which will be skipped when calculating contexts. +# +# - You can disable context calculators by either: +# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator) +# => specifying a sub-section of the Java package used by the calculator (e.g. com.example) +disabled-context-calculators: [] + # Allows you to set "aliases" for the worlds sent forward for context calculation. # # - These aliases are provided in addition to the real world name. Applied recursively. From 10f698b70fe768f48696c4454a85c9d7d780522d Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 27 Nov 2021 17:58:10 +0000 Subject: [PATCH 041/314] Add warning for common editor mistakes --- bukkit/src/main/resources/luckperms.commodore | 3 + .../commands/misc/ApplyEditsCommand.java | 4 +- .../luckperms/common/locale/Message.java | 36 ++++++++++ .../plugin/AbstractLuckPermsPlugin.java | 8 +++ .../common/plugin/LuckPermsPlugin.java | 8 +++ .../common/webeditor/SessionState.java | 48 +++++++++++++ .../common/webeditor/WebEditorRequest.java | 2 + .../common/webeditor/WebEditorResponse.java | 37 +++++++++- .../webeditor/WebEditorSessionStore.java | 67 +++++++++++++++++++ .../main/resources/luckperms_en.properties | 5 ++ 10 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java diff --git a/bukkit/src/main/resources/luckperms.commodore b/bukkit/src/main/resources/luckperms.commodore index c74b52525..1d922effd 100644 --- a/bukkit/src/main/resources/luckperms.commodore +++ b/bukkit/src/main/resources/luckperms.commodore @@ -54,6 +54,9 @@ luckperms { translations { install; } + applyedits { + code brigadier:string single_word; + } creategroup { name brigadier:string single_word { weight brigadier:integer { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java index dec561285..90c584d03 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java @@ -47,6 +47,8 @@ public ApplyEditsCommand() { @Override public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, String label) { + boolean ignoreSessionWarning = args.remove("--force"); + String code = args.get(0); if (code.isEmpty()) { @@ -71,7 +73,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St return; } - new WebEditorResponse(data).apply(plugin, sender); + new WebEditorResponse(code, data).apply(plugin, sender, label, ignoreSessionWarning); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index f6c51ae5c..20da8fa5e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -844,6 +844,42 @@ static TextComponent prefixed(ComponentLike component) { }) .build(); + Args2 APPLY_EDITS_SESSION_UNKNOWN = (code, label) -> join(newline(), + // "&4The changes received from the web editor were not made in a session started on this server!" + // "&cAre you sure you're running the /lp applyedits command in the right place?" + // "&cTo ignore this warning and apply the changes anyway, run: &4/lp applyedits --force" + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.unknown-session") + .color(DARK_RED)), + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.right-server-question") + .color(RED) + .args(text("/" + label + " applyedits"))), + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.bypass-warning") + .color(RED) + .append(text(": ")) + .append(text("/" + label + " applyedits " + code + " --force", DARK_RED))) + ); + + Args2 APPLY_EDITS_SESSION_APPLIED_ALREADY = (code, label) -> join(newline(), + // "&4The changes received from the web editor are based on an initial session which has already been applied!" + // "&cTo avoid conflicts, you should never re-use the same editor session after the changes from it have been applied once already." + // "&cTo ignore this warning and apply the changes anyway, run: /lp applyedits --force" + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.already-applied") + .color(DARK_RED)), + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.how-to-avoid-conflicts") + .color(RED) + .append(FULL_STOP)), + prefixed(translatable() + .key("luckperms.command.editor.apply-edits.bypass-warning") + .color(RED) + .append(text(": ")) + .append(text("/" + label + " applyedits " + code + " --force"))) + ); + Args1 APPLY_EDITS_INVALID_CODE = code -> prefixed(text() // "&cInvalid code. &7({})" .color(RED) diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index f058a478d..871fefa99 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -57,6 +57,7 @@ import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; +import me.lucko.luckperms.common.webeditor.WebEditorSessionStore; import net.luckperms.api.LuckPerms; @@ -89,6 +90,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { private LogDispatcher logDispatcher; private LuckPermsConfiguration configuration; private BytebinClient bytebin; + private WebEditorSessionStore webEditorSessionStore; private TranslationRepository translationRepository; private FileWatcher fileWatcher = null; private Storage storage; @@ -134,6 +136,7 @@ public final void enable() { .build(); this.bytebin = new BytebinClient(httpClient, getConfiguration().get(ConfigKeys.BYTEBIN_URL), "luckperms"); + this.webEditorSessionStore = new WebEditorSessionStore(); // init translation repo and update bundle files this.translationRepository = new TranslationRepository(this); @@ -416,6 +419,11 @@ public BytebinClient getBytebin() { return this.bytebin; } + @Override + public WebEditorSessionStore getWebEditorSessionStore() { + return this.webEditorSessionStore; + } + @Override public TranslationRepository getTranslationRepository() { return this.translationRepository; diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java index 25fc4b544..89e83ada1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java @@ -55,6 +55,7 @@ import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; +import me.lucko.luckperms.common.webeditor.WebEditorSessionStore; import net.luckperms.api.query.QueryOptions; @@ -248,6 +249,13 @@ public interface LuckPermsPlugin { */ BytebinClient getBytebin(); + /** + * Gets the web editor session store + * + * @return the web editor session store + */ + WebEditorSessionStore getWebEditorSessionStore(); + /** * Gets a calculated context instance for the user using the rules of the platform. * diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java new file mode 100644 index 000000000..a99574357 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java @@ -0,0 +1,48 @@ +/* + * 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.webeditor; + +/** + * Represents the state of a web editor session + */ +enum SessionState { + + /** + * The session is not known to this server. + */ + NOT_KNOWN, + + /** + * The session is in progress - it has been created, but updates have not been applied. + */ + IN_PROGRESS, + + /** + * The session is complete - updates have been applied. + */ + COMPLETED + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java index 868303c57..24ddbb252 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java @@ -181,6 +181,8 @@ public void createSession(LuckPermsPlugin plugin, Sender sender) { return; } + plugin.getWebEditorSessionStore().addNewSession(pasteId); + // form a url for the editor String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + pasteId; Message.EDITOR_URL.send(sender, url); diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java index 6791f56a1..b90bcd967 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java @@ -65,12 +65,18 @@ */ public class WebEditorResponse { + /** + * The id of the response payload + */ + private final String id; + /** * The encoded json object this payload is made up of */ private final JsonObject payload; - public WebEditorResponse(JsonObject payload) { + public WebEditorResponse(String id, JsonObject payload) { + this.id = id; this.payload = payload; } @@ -80,7 +86,34 @@ public WebEditorResponse(JsonObject payload) { * @param plugin the plugin * @param sender the sender who is applying the session */ - public void apply(LuckPermsPlugin plugin, Sender sender) { + public void apply(LuckPermsPlugin plugin, Sender sender, String commandLabel, boolean ignoreSessionWarning) { + JsonElement sessionIdJson = this.payload.get("sessionId"); + if (sessionIdJson != null) { + String sessionId = sessionIdJson.getAsString(); + WebEditorSessionStore sessionStore = plugin.getWebEditorSessionStore(); + + SessionState state = sessionStore.getSessionState(sessionId); + switch (state) { + case COMPLETED: + if (!ignoreSessionWarning) { + Message.APPLY_EDITS_SESSION_APPLIED_ALREADY.send(sender, this.id, commandLabel); + return; + } + break; + case NOT_KNOWN: + if (!ignoreSessionWarning) { + Message.APPLY_EDITS_SESSION_UNKNOWN.send(sender, this.id, commandLabel); + return; + } + break; + case IN_PROGRESS: + sessionStore.markSessionCompleted(sessionId); + break; + default: + throw new AssertionError(state); + } + } + Session session = new Session(plugin, sender); boolean work = false; diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java new file mode 100644 index 000000000..be7d897f8 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java @@ -0,0 +1,67 @@ +/* + * 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.webeditor; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Contains a store of known web editor sessions. + */ +public class WebEditorSessionStore { + private final Map sessions = new ConcurrentHashMap<>(); + + /** + * Adds a newly created session to the store. + * + * @param id the id of the session + */ + public void addNewSession(String id) { + this.sessions.put(id, SessionState.IN_PROGRESS); + } + + /** + * Gets the session state for the given session id. + * + * @param id the id of the session + * @return the session state + */ + public @NonNull SessionState getSessionState(String id) { + return this.sessions.getOrDefault(id, SessionState.NOT_KNOWN); + } + + /** + * Marks a given session as complete. + * + * @param id the id of the session + */ + public void markSessionCompleted(String id) { + this.sessions.put(id, SessionState.COMPLETED); + } + +} diff --git a/common/src/main/resources/luckperms_en.properties b/common/src/main/resources/luckperms_en.properties index 0d5202ad5..e85b99eef 100644 --- a/common/src/main/resources/luckperms_en.properties +++ b/common/src/main/resources/luckperms_en.properties @@ -93,6 +93,11 @@ luckperms.command.editor.apply-edits.success.deletions-singular=deletion luckperms.command.editor.apply-edits.no-changes=No changes were applied from the web editor, the returned data didn''t contain any edits luckperms.command.editor.apply-edits.unknown-type=Unable to apply edit to the specified object type luckperms.command.editor.apply-edits.unable-to-read=Unable to read data using the given code +luckperms.command.editor.apply-edits.unknown-session=The changes received from the web editor were not made in a session started on this server! +luckperms.command.editor.apply-edits.right-server-question=Are you sure you''re running the {0} command in the right place? +luckperms.command.editor.apply-edits.already-applied=The changes received from the web editor are based on an initial session which has already been applied! +luckperms.command.editor.apply-edits.how-to-avoid-conflicts=To avoid conflicts, you should never re-use the same editor session after the changes from it have been applied once already +luckperms.command.editor.apply-edits.bypass-warning=To ignore this warning and apply the changes anyway, run luckperms.command.search.searching.permission=Searching for users and groups with {0} luckperms.command.search.searching.inherit=Searching for users and groups who inherit from {0} luckperms.command.search.result=Found {0} entries from {1} users and {2} groups From d6c810aa71f5bbec26381d4494f01fe6e39ee86b Mon Sep 17 00:00:00 2001 From: Tadhg Boyle Date: Sat, 27 Nov 2021 14:48:22 -0800 Subject: [PATCH 042/314] Fix player insert duplicate key issue with PSQL (#3223) --- .../storage/implementation/sql/SqlStorage.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index f1170da0e..23a08e779 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -94,7 +94,10 @@ public class SqlStorage implements StorageImplementation { private static final String PLAYER_SELECT_UUID_BY_USERNAME = "SELECT uuid FROM '{prefix}players' WHERE username=? LIMIT 1"; private static final String PLAYER_SELECT_USERNAME_BY_UUID = "SELECT username FROM '{prefix}players' WHERE uuid=? LIMIT 1"; private static final String PLAYER_UPDATE_USERNAME_FOR_UUID = "UPDATE '{prefix}players' SET username=? WHERE uuid=?"; - private static final String PLAYER_INSERT = "INSERT INTO '{prefix}players' (uuid, username, primary_group) VALUES(?, ?, ?)"; + private static final Map PLAYER_INSERT = ImmutableMap.of( + "PostgreSQL", "INSERT INTO '{prefix}players' (uuid, username, primary_group) VALUES(?, ?, ?) ON CONFLICT DO UPDATE SET username=?, primary_group=?" + ); + private static final String PLAYER_INSERT_DEFAULT = "INSERT INTO '{prefix}players' (uuid, username, primary_group) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE username=?, primary_group=?"; private static final String PLAYER_DELETE = "DELETE FROM '{prefix}players' WHERE uuid=?"; private static final String PLAYER_SELECT_ALL_UUIDS_BY_USERNAME = "SELECT uuid FROM '{prefix}players' WHERE username=? AND NOT uuid=?"; private static final String PLAYER_DELETE_ALL_UUIDS_BY_USERNAME = "DELETE FROM '{prefix}players' WHERE username=? AND NOT uuid=?"; @@ -621,10 +624,12 @@ public PlayerSaveResult savePlayerData(UUID uniqueId, String username) throws SQ ps.execute(); } } else { - try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT))) { + try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT.getOrDefault(this.connectionFactory.getImplementationName(), PLAYER_INSERT_DEFAULT)))) { ps.setString(1, uniqueId.toString()); ps.setString(2, username); ps.setString(3, GroupManager.DEFAULT_GROUP_NAME); + ps.setString(4, username); + ps.setString(5, GroupManager.DEFAULT_GROUP_NAME); ps.execute(); } } @@ -897,10 +902,12 @@ private void insertPlayerData(Connection c, UUID user, SqlPlayerData data) throw } } else { // insert - try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT))) { + try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT.getOrDefault(this.connectionFactory.getImplementationName(), PLAYER_INSERT_DEFAULT)))) { ps.setString(1, user.toString()); ps.setString(2, data.username); ps.setString(3, data.primaryGroup); + ps.setString(4, data.username); + ps.setString(5, data.primaryGroup); ps.execute(); } } From 6e07d6ad366f0ba52b3f81a3bbd5499ee2500b61 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 28 Nov 2021 10:29:55 +0000 Subject: [PATCH 043/314] Revert "Fix player insert duplicate key issue with PSQL (#3223)" This reverts commit d6c810aa71f5bbec26381d4494f01fe6e39ee86b. --- .../storage/implementation/sql/SqlStorage.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index 23a08e779..f1170da0e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -94,10 +94,7 @@ public class SqlStorage implements StorageImplementation { private static final String PLAYER_SELECT_UUID_BY_USERNAME = "SELECT uuid FROM '{prefix}players' WHERE username=? LIMIT 1"; private static final String PLAYER_SELECT_USERNAME_BY_UUID = "SELECT username FROM '{prefix}players' WHERE uuid=? LIMIT 1"; private static final String PLAYER_UPDATE_USERNAME_FOR_UUID = "UPDATE '{prefix}players' SET username=? WHERE uuid=?"; - private static final Map PLAYER_INSERT = ImmutableMap.of( - "PostgreSQL", "INSERT INTO '{prefix}players' (uuid, username, primary_group) VALUES(?, ?, ?) ON CONFLICT DO UPDATE SET username=?, primary_group=?" - ); - private static final String PLAYER_INSERT_DEFAULT = "INSERT INTO '{prefix}players' (uuid, username, primary_group) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE username=?, primary_group=?"; + private static final String PLAYER_INSERT = "INSERT INTO '{prefix}players' (uuid, username, primary_group) VALUES(?, ?, ?)"; private static final String PLAYER_DELETE = "DELETE FROM '{prefix}players' WHERE uuid=?"; private static final String PLAYER_SELECT_ALL_UUIDS_BY_USERNAME = "SELECT uuid FROM '{prefix}players' WHERE username=? AND NOT uuid=?"; private static final String PLAYER_DELETE_ALL_UUIDS_BY_USERNAME = "DELETE FROM '{prefix}players' WHERE username=? AND NOT uuid=?"; @@ -624,12 +621,10 @@ public PlayerSaveResult savePlayerData(UUID uniqueId, String username) throws SQ ps.execute(); } } else { - try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT.getOrDefault(this.connectionFactory.getImplementationName(), PLAYER_INSERT_DEFAULT)))) { + try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT))) { ps.setString(1, uniqueId.toString()); ps.setString(2, username); ps.setString(3, GroupManager.DEFAULT_GROUP_NAME); - ps.setString(4, username); - ps.setString(5, GroupManager.DEFAULT_GROUP_NAME); ps.execute(); } } @@ -902,12 +897,10 @@ private void insertPlayerData(Connection c, UUID user, SqlPlayerData data) throw } } else { // insert - try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT.getOrDefault(this.connectionFactory.getImplementationName(), PLAYER_INSERT_DEFAULT)))) { + try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT))) { ps.setString(1, user.toString()); ps.setString(2, data.username); ps.setString(3, data.primaryGroup); - ps.setString(4, data.username); - ps.setString(5, data.primaryGroup); ps.execute(); } } From e227d96cbfae4712a583213afcecf8d859747246 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 28 Nov 2021 10:46:48 +0000 Subject: [PATCH 044/314] Fix sql insert player query (#3225) --- .../implementation/sql/SqlStorage.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index f1170da0e..1e16da190 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -98,7 +98,7 @@ public class SqlStorage implements StorageImplementation { private static final String PLAYER_DELETE = "DELETE FROM '{prefix}players' WHERE uuid=?"; private static final String PLAYER_SELECT_ALL_UUIDS_BY_USERNAME = "SELECT uuid FROM '{prefix}players' WHERE username=? AND NOT uuid=?"; private static final String PLAYER_DELETE_ALL_UUIDS_BY_USERNAME = "DELETE FROM '{prefix}players' WHERE username=? AND NOT uuid=?"; - private static final String PLAYER_SELECT_BY_UUID = "SELECT username, primary_group FROM '{prefix}players' WHERE uuid=?"; + private static final String PLAYER_SELECT_BY_UUID = "SELECT username, primary_group FROM '{prefix}players' WHERE uuid=? LIMIT 1"; private static final String PLAYER_SELECT_PRIMARY_GROUP_BY_UUID = "SELECT primary_group FROM '{prefix}players' WHERE uuid=? LIMIT 1"; private static final String PLAYER_UPDATE_PRIMARY_GROUP_BY_UUID = "UPDATE '{prefix}players' SET primary_group=? WHERE uuid=?"; @@ -607,26 +607,25 @@ public void deleteTrack(Track track) throws SQLException { @Override public PlayerSaveResult savePlayerData(UUID uniqueId, String username) throws SQLException { username = username.toLowerCase(Locale.ROOT); + String oldUsername = null; - // find any existing mapping - String oldUsername = getPlayerName(uniqueId); - - // do the insert - if (!username.equals(oldUsername)) { - try (Connection c = this.connectionFactory.getConnection()) { - if (oldUsername != null) { + try (Connection c = this.connectionFactory.getConnection()) { + SqlPlayerData existingPlayerData = selectPlayerData(c, uniqueId); + if (existingPlayerData == null) { + try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT))) { + ps.setString(1, uniqueId.toString()); + ps.setString(2, username); + ps.setString(3, GroupManager.DEFAULT_GROUP_NAME); + ps.execute(); + } + } else { + oldUsername = existingPlayerData.username; + if (!username.equals(oldUsername)) { try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_UPDATE_USERNAME_FOR_UUID))) { ps.setString(1, username); ps.setString(2, uniqueId.toString()); ps.execute(); } - } else { - try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(PLAYER_INSERT))) { - ps.setString(1, uniqueId.toString()); - ps.setString(2, username); - ps.setString(3, GroupManager.DEFAULT_GROUP_NAME); - ps.execute(); - } } } } From 64bd8b5eaface432e9030a71cd5adcb751c1837e Mon Sep 17 00:00:00 2001 From: Tadhg Boyle Date: Tue, 30 Nov 2021 11:52:43 -0800 Subject: [PATCH 045/314] Add pagination to listgroups command (#3226) --- bukkit/src/main/resources/luckperms.commodore | 4 ++- .../common/command/spec/CommandSpec.java | 5 +-- .../common/commands/group/ListGroups.java | 35 ++++++++++++++----- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/bukkit/src/main/resources/luckperms.commodore b/bukkit/src/main/resources/luckperms.commodore index 1d922effd..5d043c1ca 100644 --- a/bukkit/src/main/resources/luckperms.commodore +++ b/bukkit/src/main/resources/luckperms.commodore @@ -69,7 +69,9 @@ luckperms { flags brigadier:string greedy_phrase; } } - listgroups; + listgroups { + page brigadier:integer; + } createtrack { name brigadier:string single_word; } diff --git a/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java b/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java index fe5a97409..c162d1b44 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java @@ -99,8 +99,9 @@ public enum CommandSpec { DELETE_GROUP("/%s deletegroup ", arg("name", true) ), - LIST_GROUPS("/%s listgroups"), - + LIST_GROUPS("/%s listgroups", + arg("page", false) + ), CREATE_TRACK("/%s createtrack ", arg("name", true) ), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/ListGroups.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/ListGroups.java index 6f77368eb..5937eae0d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/ListGroups.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/ListGroups.java @@ -30,17 +30,20 @@ import me.lucko.luckperms.common.command.spec.CommandSpec; import me.lucko.luckperms.common.command.utils.ArgumentList; import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.Iterators; import me.lucko.luckperms.common.util.Predicates; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; public class ListGroups extends SingleCommand { public ListGroups() { - super(CommandSpec.LIST_GROUPS, "ListGroups", CommandPermission.LIST_GROUPS, Predicates.alwaysFalse()); + super(CommandSpec.LIST_GROUPS, "ListGroups", CommandPermission.LIST_GROUPS, Predicates.notInRange(0, 1)); } @Override @@ -53,15 +56,29 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St return; } - Message.GROUPS_LIST.send(sender); - plugin.getGroupManager().getAll().values().stream() - .sorted((o1, o2) -> { + int page = args.getIntOrDefault(0, 1); + int pageIndex = page - 1; + + List groups = plugin.getGroupManager().getAll().values().stream().sorted((o1, o2) -> { int i = Integer.compare(o2.getWeight().orElse(0), o1.getWeight().orElse(0)); return i != 0 ? i : o1.getName().compareToIgnoreCase(o2.getName()); - }) - .forEach(group -> { - List tracks = plugin.getTrackManager().getAll().values().stream().filter(t -> t.containsGroup(group)).map(Track::getName).collect(Collectors.toList()); - Message.GROUPS_LIST_ENTRY.send(sender, group, group.getWeight().orElse(0), tracks); - }); + }).collect(Collectors.toList()); + + List> pages = Iterators.divideIterable(groups, 8); + + if (pageIndex < 0 || pageIndex >= pages.size()) { + page = 1; + pageIndex = 0; + } + + Message.SEARCH_SHOWING_GROUPS.send(sender, page, pages.size(), groups.size()); + Message.GROUPS_LIST.send(sender); + + Collection allTracks = plugin.getTrackManager().getAll().values(); + + for (Group group : pages.get(pageIndex)) { + List tracks = allTracks.stream().filter(t -> t.containsGroup(group)).map(Track::getName).collect(Collectors.toList()); + Message.GROUPS_LIST_ENTRY.send(sender, group, group.getWeight().orElse(0), tracks); + } } } From fa1740413398e8fc9f28e744e5c4567b8cae56fb Mon Sep 17 00:00:00 2001 From: Luck Date: Tue, 30 Nov 2021 20:03:16 +0000 Subject: [PATCH 046/314] Fabric 1.18 --- fabric/build.gradle | 8 ++-- .../context/FabricPlayerCalculator.java | 2 +- .../ClientSettingsC2SPacketAccessor.java | 39 ------------------- .../fabric/mixin/ServerPlayerEntityMixin.java | 6 +-- .../src/main/resources/luckperms.mixins.json | 5 +-- 5 files changed, 10 insertions(+), 50 deletions(-) delete mode 100644 fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ClientSettingsC2SPacketAccessor.java diff --git a/fabric/build.gradle b/fabric/build.gradle index 4b51f2bd6..7c6cda7b4 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -14,9 +14,9 @@ repositories { dependencies { // https://modmuss50.me/fabric.html - minecraft 'com.mojang:minecraft:1.17.1' - mappings 'net.fabricmc:yarn:1.17.1+build.39:v2' - modImplementation 'net.fabricmc:fabric-loader:0.11.6' + minecraft 'com.mojang:minecraft:1.18' + mappings 'net.fabricmc:yarn:1.18+build.1:v2' + modImplementation 'net.fabricmc:fabric-loader:0.12.6' Set apiModules = [ 'fabric-api-base', @@ -26,7 +26,7 @@ dependencies { ] apiModules.forEach { - modImplementation(fabricApi.module(it, '0.38.0+1.17')) + modImplementation(fabricApi.module(it, '0.43.1+1.18')) } include(modImplementation('me.lucko:fabric-permissions-api:0.1-SNAPSHOT')) diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java index 193e73e3c..e964e9e7a 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/context/FabricPlayerCalculator.java @@ -81,7 +81,7 @@ public void calculate(@NonNull ServerPlayerEntity target, @NonNull ContextConsum } // TODO: figure out dimension type context too - ServerWorld world = target.getServerWorld(); + ServerWorld world = target.getWorld(); if (this.world) { this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).rewriteAndSubmit(getContextKey(world.getRegistryKey().getValue()), consumer); } diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ClientSettingsC2SPacketAccessor.java b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ClientSettingsC2SPacketAccessor.java deleted file mode 100644 index 929006fca..000000000 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ClientSettingsC2SPacketAccessor.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.fabric.mixin; - -import net.minecraft.network.packet.c2s.play.ClientSettingsC2SPacket; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(ClientSettingsC2SPacket.class) -public interface ClientSettingsC2SPacketAccessor { - - @Accessor("language") - String getLanguage(); - -} diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java index cc30d1e9c..3232599ee 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java @@ -70,7 +70,7 @@ public abstract class ServerPlayerEntityMixin implements MixinUser { private Locale luckperms$locale; // Used by PlayerChangeWorldCallback hook below. - @Shadow public abstract ServerWorld getServerWorld(); + @Shadow public abstract ServerWorld getWorld(); @Override public User getLuckPermsUser() { @@ -148,12 +148,12 @@ private void luckperms_copyFrom(ServerPlayerEntity oldPlayer, boolean alive, Cal @Inject(at = @At("HEAD"), method = "setClientSettings") private void luckperms_setClientSettings(ClientSettingsC2SPacket information, CallbackInfo ci) { - String language = ((ClientSettingsC2SPacketAccessor) information).getLanguage(); + String language = information.language(); this.luckperms$locale = TranslationManager.parseLocale(language); } @Inject(at = @At("TAIL"), method = "worldChanged") private void luckperms_onChangeDimension(ServerWorld targetWorld, CallbackInfo ci) { - PlayerChangeWorldCallback.EVENT.invoker().onChangeWorld(this.getServerWorld(), targetWorld, (ServerPlayerEntity) (Object) this); + PlayerChangeWorldCallback.EVENT.invoker().onChangeWorld(this.getWorld(), targetWorld, (ServerPlayerEntity) (Object) this); } } diff --git a/fabric/src/main/resources/luckperms.mixins.json b/fabric/src/main/resources/luckperms.mixins.json index 392d4dac7..24634c967 100644 --- a/fabric/src/main/resources/luckperms.mixins.json +++ b/fabric/src/main/resources/luckperms.mixins.json @@ -3,10 +3,9 @@ "package": "me.lucko.luckperms.fabric.mixin", "compatibilityLevel": "JAVA_8", "mixins": [ - "ClientSettingsC2SPacketAccessor", + "CommandManagerMixin", "ServerLoginNetworkHandlerAccessor", - "ServerPlayerEntityMixin", - "CommandManagerMixin" + "ServerPlayerEntityMixin" ], "client": [ ], From f35a9b6a0986033e4cefb476eb921234f361d9f0 Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 2 Dec 2021 13:24:02 +0000 Subject: [PATCH 047/314] Update dependencies for 1.18 --- bukkit/build.gradle | 4 ++-- bungee/build.gradle | 2 +- common/build.gradle | 8 ++++---- .../common/dependencies/Dependency.java | 20 +++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/bukkit/build.gradle b/bukkit/build.gradle index c040822ff..25b2e5096 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -12,12 +12,12 @@ dependencies { compileOnly project(':common:loader-utils') compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT' - compileOnly('me.lucko:adventure-platform-bukkit:4.8.1') { + compileOnly('me.lucko:adventure-platform-bukkit:4.9.4') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') } - compileOnly 'me.lucko:commodore:1.10' + compileOnly 'me.lucko:commodore:1.11' compileOnly('net.milkbowl.vault:VaultAPI:1.7') { exclude(module: 'bukkit') } diff --git a/bungee/build.gradle b/bungee/build.gradle index bc9dd8dbb..33ea496e8 100644 --- a/bungee/build.gradle +++ b/bungee/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly project(':common:loader-utils') compileOnly 'net.md-5:bungeecord-api:1.16-R0.4' - compileOnly('me.lucko:adventure-platform-bungeecord:4.8.1') { + compileOnly('me.lucko:adventure-platform-bungeecord:4.9.4') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') diff --git a/common/build.gradle b/common/build.gradle index d819bf38a..e4c43c1e2 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -20,24 +20,24 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' compileOnly 'org.apache.logging.log4j:log4j-api:2.14.0' - api('net.kyori:adventure-api:4.8.1') { + api('net.kyori:adventure-api:4.9.3') { exclude(module: 'adventure-bom') exclude(module: 'checker-qual') exclude(module: 'annotations') } - api('net.kyori:adventure-text-serializer-gson:4.8.1') { + api('net.kyori:adventure-text-serializer-gson:4.9.3') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'gson') } - api('net.kyori:adventure-text-serializer-legacy:4.8.1') { + api('net.kyori:adventure-text-serializer-legacy:4.9.3') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } - api('net.kyori:adventure-text-serializer-plain:4.8.1') { + api('net.kyori:adventure-text-serializer-plain:4.9.3') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index 9b21961c6..c83b9c6f1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -64,29 +64,29 @@ public enum Dependency { ADVENTURE( "me{}lucko", "adventure-api", - "4.8.1", - "S0Txix9BuhCD8pztmpDvbWqgKflNNqO/xBdXoNxrJSU=", + "4.9.4", + "bh1cB0ZdzxstqRfVowKbgAZM16T/CtJ7oIJCaknbGeM=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM( "me{}lucko", "adventure-platform-api", - "4.8.1", - "1LAWziMjueMDuNDNv1QqqJRt40inA9H7b3rI5cPc+ZU=", + "4.9.4", + "CZVyxNOi7ln7dgldfDs1m1M6d9w/bKG8Rp27qfAuTJw=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUKKIT( "me{}lucko", "adventure-platform-bukkit", - "4.8.1", - "0YYq9QFRJBaPCOS4CVTCkT6BDAsnK38FaRPl6oc4sU8=", + "4.9.4", + "JkA3GGkf3pADA3PACYfdVlAglstgzz+ajAxElMXXn6I=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUNGEECORD( "me{}lucko", "adventure-platform-bungeecord", - "4.8.1", - "hmCpfgWQHMpDfuOji+TVp2GtKxSqgOLzy7bOFb32i7g=", + "4.9.4", + "a/p8/eIjbQnNlN5cJ/yIUjttZyJ/GRXgcsg8rhY7SwI=", Relocation.of("adventure", "net{}kyori{}adventure") ), EVENT( @@ -128,8 +128,8 @@ public enum Dependency { COMMODORE( "me{}lucko", "commodore", - "1.10", - "DJi8ZLaSwhoU49UBrIFzVBksaVAj8bthtVCnhuBPRz4=", + "1.11", + "TxlkwmLE04ipLfI4HSyC0mzYt/8gTSDBpfCSk+kiaZI=", Relocation.of("commodore", "me{}lucko{}commodore") ), COMMODORE_FILE( From 9c0874162bad080ebb5adc44a74d332508a86731 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 4 Dec 2021 10:41:12 +0000 Subject: [PATCH 048/314] Fix cast to long in MongoStorage --- .../common/storage/implementation/mongodb/MongoStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java index bf595a97a..574df62f2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java @@ -667,7 +667,7 @@ private static Node nodeFromDoc(Document document) { } if (document.containsKey("expiry")) { - builder.expiry(document.getLong("expiry")); + builder.expiry((long) document.get("expiry")); } if (document.containsKey("context") && document.get("context") instanceof List) { From 8cc126f71160ba2d475ac5abad43a8fc9daabc99 Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 9 Dec 2021 21:31:17 +0000 Subject: [PATCH 049/314] Add config option to disable bulkupdate functionality --- bukkit/src/main/resources/config.yml | 6 ++++++ bungee/src/main/resources/config.yml | 6 ++++++ .../luckperms/common/commands/misc/BulkUpdateCommand.java | 5 +++++ .../java/me/lucko/luckperms/common/config/ConfigKeys.java | 5 +++++ .../java/me/lucko/luckperms/common/locale/Message.java | 7 +++++++ common/src/main/resources/luckperms_en.properties | 1 + fabric/src/main/resources/luckperms.conf | 6 ++++++ nukkit/src/main/resources/config.yml | 6 ++++++ sponge/src/main/resources/luckperms.conf | 6 ++++++ velocity/src/main/resources/config.yml | 6 ++++++ 10 files changed, 54 insertions(+) diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index a31dc3060..cb1167ece 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -687,6 +687,12 @@ allow-invalid-usernames: false # - If automation is needed, users should prefer using the LuckPerms API. skip-bulkupdate-confirmation: false +# If LuckPerms should prevent bulkupdate operations. +# +# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work. +# - When set to false, bulkupdate operations will be allowed via the console. +disable-bulkupdate: false + # If LuckPerms should allow a users primary group to be removed with the 'parent remove' command. # # - When this happens, the plugin will set their primary group back to default. diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index 53a2bd837..d90c33588 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -589,6 +589,12 @@ allow-invalid-usernames: false # - If automation is needed, users should prefer using the LuckPerms API. skip-bulkupdate-confirmation: false +# If LuckPerms should prevent bulkupdate operations. +# +# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work. +# - When set to false, bulkupdate operations will be allowed via the console. +disable-bulkupdate: false + # If LuckPerms should allow a users primary group to be removed with the 'parent remove' command. # # - When this happens, the plugin will set their primary group back to default. diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java index 85feafb78..7c6b63d15 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/BulkUpdateCommand.java @@ -64,6 +64,11 @@ public BulkUpdateCommand() { @Override public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, String label) throws CommandException { + if (plugin.getConfiguration().get(ConfigKeys.DISABLE_BULKUPDATE)) { + Message.BULK_UPDATE_DISABLED.send(sender); + return; + } + if (!sender.isConsole()) { Message.BULK_UPDATE_MUST_USE_CONSOLE.send(sender); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 11eee3e89..970ca9760 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -164,6 +164,11 @@ private ConfigKeys() {} */ public static final ConfigKey SKIP_BULKUPDATE_CONFIRMATION = booleanKey("skip-bulkupdate-confirmation", false); + /** + * If LuckPerms should prevent bulkupdate operations. + */ + public static final ConfigKey DISABLE_BULKUPDATE = booleanKey("disable-bulkupdate", false); + /** * If LuckPerms should produce extra logging output when it handles logins. */ diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index 20da8fa5e..6976bd500 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -2662,6 +2662,13 @@ static TextComponent prefixed(ComponentLike component) { .append(FULL_STOP) ); + Args0 BULK_UPDATE_DISABLED = () -> prefixed(translatable() + // "&cBulk update functionality is disabled in the configuration file." + .key("luckperms.command.bulkupdate.disabled") + .color(RED) + .append(FULL_STOP) + ); + Args0 BULK_UPDATE_MUST_USE_CONSOLE = () -> prefixed(translatable() // "&cThe bulk update command can only be used from the console." .key("luckperms.command.bulkupdate.must-use-console") diff --git a/common/src/main/resources/luckperms_en.properties b/common/src/main/resources/luckperms_en.properties index e85b99eef..c587e836d 100644 --- a/common/src/main/resources/luckperms_en.properties +++ b/common/src/main/resources/luckperms_en.properties @@ -324,6 +324,7 @@ luckperms.command.import.progress.operations={0}/{1} operations complete luckperms.command.import.starting=Starting import process luckperms.command.import.completed=COMPLETED luckperms.command.import.duration=took {0} seconds +luckperms.command.bulkupdate.disabled=Bulk update functionality is disabled in the configuration file luckperms.command.bulkupdate.must-use-console=The bulk update command can only be used from the console luckperms.command.bulkupdate.invalid-data-type=Invalid type, was expecting {0} luckperms.command.bulkupdate.invalid-constraint=Invalid constraint {0} diff --git a/fabric/src/main/resources/luckperms.conf b/fabric/src/main/resources/luckperms.conf index f875e3e5a..73c0825c0 100644 --- a/fabric/src/main/resources/luckperms.conf +++ b/fabric/src/main/resources/luckperms.conf @@ -620,6 +620,12 @@ allow-invalid-usernames = false # - If automation is needed, users should prefer using the LuckPerms API. skip-bulkupdate-confirmation = false +# If LuckPerms should prevent bulkupdate operations. +# +# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work. +# - When set to false, bulkupdate operations will be allowed via the console. +disable-bulkupdate = false + # If LuckPerms should allow a users primary group to be removed with the 'parent remove' command. # # - When this happens, the plugin will set their primary group back to default. diff --git a/nukkit/src/main/resources/config.yml b/nukkit/src/main/resources/config.yml index 6d9e2d068..a20e4fe03 100644 --- a/nukkit/src/main/resources/config.yml +++ b/nukkit/src/main/resources/config.yml @@ -631,6 +631,12 @@ allow-invalid-usernames: true # - If automation is needed, users should prefer using the LuckPerms API. skip-bulkupdate-confirmation: false +# If LuckPerms should prevent bulkupdate operations. +# +# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work. +# - When set to false, bulkupdate operations will be allowed via the console. +disable-bulkupdate: false + # If LuckPerms should allow a users primary group to be removed with the 'parent remove' command. # # - When this happens, the plugin will set their primary group back to default. diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 0f58a2775..ac04c48ba 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -607,6 +607,12 @@ allow-invalid-usernames = false # - If automation is needed, users should prefer using the LuckPerms API. skip-bulkupdate-confirmation = false +# If LuckPerms should prevent bulkupdate operations. +# +# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work. +# - When set to false, bulkupdate operations will be allowed via the console. +disable-bulkupdate = false + # If LuckPerms should allow a users primary group to be removed with the 'parent remove' command. # # - When this happens, the plugin will set their primary group back to default. diff --git a/velocity/src/main/resources/config.yml b/velocity/src/main/resources/config.yml index 7321eabf5..e6eb8c7d8 100644 --- a/velocity/src/main/resources/config.yml +++ b/velocity/src/main/resources/config.yml @@ -575,6 +575,12 @@ allow-invalid-usernames: false # - If automation is needed, users should prefer using the LuckPerms API. skip-bulkupdate-confirmation: false +# If LuckPerms should prevent bulkupdate operations. +# +# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work. +# - When set to false, bulkupdate operations will be allowed via the console. +disable-bulkupdate: false + # If LuckPerms should allow a users primary group to be removed with the 'parent remove' command. # # - When this happens, the plugin will set their primary group back to default. From 664cad21a9c24954503a78b44ae218d13353a465 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 10 Dec 2021 23:47:06 +0000 Subject: [PATCH 050/314] Clarify uuid type in user info command (#3233) --- .../lookup/UniqueIdDetermineTypeEvent.java | 16 +++++ .../luckperms/common/util/UniqueIdType.java | 58 +++++++++++++++---- .../main/resources/luckperms_en.properties | 9 ++- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdDetermineTypeEvent.java b/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdDetermineTypeEvent.java index 78bec2de9..faec60cb9 100644 --- a/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdDetermineTypeEvent.java +++ b/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdDetermineTypeEvent.java @@ -56,6 +56,22 @@ public interface UniqueIdDetermineTypeEvent extends LuckPermsEvent, ResultEvent< */ String TYPE_UNAUTHENTICATED = "unauthenticated"; + /** + * The players UUID most likely belongs to a NPC (non-player character). + * + *

Usually indicated by the UUID being {@link UUID#version() version} 2.

+ * + * @since 5.4 + */ + String TYPE_NPC = "npc"; + + /** + * Unknown UUID type. + * + * @since 5.4 + */ + String TYPE_UNKNOWN = "unknown"; + /** * Gets the {@link UUID unique id} being queried. * diff --git a/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java b/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java index b3dbfdd89..6e19428c2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java @@ -28,7 +28,9 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; import net.luckperms.api.event.player.lookup.UniqueIdDetermineTypeEvent; import java.util.UUID; @@ -40,16 +42,31 @@ public final class UniqueIdType { public static final UniqueIdType AUTHENTICATED = new UniqueIdType( UniqueIdDetermineTypeEvent.TYPE_AUTHENTICATED, - Component.translatable("luckperms.command.user.info.uuid-type.mojang", NamedTextColor.DARK_GREEN) + NamedTextColor.DARK_GREEN, + "luckperms.command.user.info.uuid-type.mojang", + "luckperms.command.user.info.uuid-type.desc.mojang" ); public static final UniqueIdType UNAUTHENTICATED = new UniqueIdType( UniqueIdDetermineTypeEvent.TYPE_UNAUTHENTICATED, - Component.translatable("luckperms.command.user.info.uuid-type.not-mojang", NamedTextColor.DARK_GRAY) + NamedTextColor.DARK_GRAY, + "luckperms.command.user.info.uuid-type.not-mojang", + "luckperms.command.user.info.uuid-type.desc.not-mojang" ); - private static final String TYPE_NPC = "npc"; - public static final UniqueIdType NPC = new UniqueIdType(TYPE_NPC); + public static final UniqueIdType NPC = new UniqueIdType( + UniqueIdDetermineTypeEvent.TYPE_NPC, + NamedTextColor.GOLD, + "luckperms.command.user.info.uuid-type.npc", + "luckperms.command.user.info.uuid-type.desc.npc" + ); + + public static final UniqueIdType UNKNOWN = new UniqueIdType( + UniqueIdDetermineTypeEvent.TYPE_UNKNOWN, + NamedTextColor.RED, + "luckperms.command.user.info.uuid-type.unknown", + "luckperms.command.user.info.uuid-type.desc.unknown" + ); public static UniqueIdType determineType(UUID uniqueId, LuckPermsPlugin plugin) { // determine initial type based on the uuid version @@ -65,10 +82,10 @@ public static UniqueIdType determineType(UUID uniqueId, LuckPermsPlugin plugin) // if the uuid is version 2, assume it is an NPC // see: https://github.com/lucko/LuckPerms/issues/1470 // and https://github.com/lucko/LuckPerms/issues/1470#issuecomment-475403162 - type = TYPE_NPC; + type = UniqueIdDetermineTypeEvent.TYPE_NPC; break; default: - type = "unknown"; + type = UniqueIdDetermineTypeEvent.TYPE_UNKNOWN; break; } @@ -80,8 +97,10 @@ public static UniqueIdType determineType(UUID uniqueId, LuckPermsPlugin plugin) return AUTHENTICATED; case UniqueIdDetermineTypeEvent.TYPE_UNAUTHENTICATED: return UNAUTHENTICATED; - case TYPE_NPC: + case UniqueIdDetermineTypeEvent.TYPE_NPC: return NPC; + case UniqueIdDetermineTypeEvent.TYPE_UNKNOWN: + return UNKNOWN; default: return new UniqueIdType(type); } @@ -90,13 +109,30 @@ public static UniqueIdType determineType(UUID uniqueId, LuckPermsPlugin plugin) private final String type; private final Component component; - private UniqueIdType(String type) { - this(type, Component.text(type, NamedTextColor.GOLD)); + // constructor used for built-in types + private UniqueIdType(String type, TextColor displayColor, String translationKey, String translationKeyHover) { + this.type = type; + this.component = Component.translatable() + .key(translationKey) + .color(displayColor) + .hoverEvent(HoverEvent.showText(Component.translatable( + translationKeyHover, + NamedTextColor.DARK_GRAY + ))) + .build(); } - private UniqueIdType(String type, Component component) { + // constructor used for types provided via the API + private UniqueIdType(String type) { this.type = type; - this.component = component; + this.component = Component.text() + .content(type) + .color(NamedTextColor.GOLD) + .hoverEvent(HoverEvent.showText(Component.translatable( + "luckperms.command.user.info.uuid-type.desc.api", + NamedTextColor.GRAY + ))) + .build(); } public String getType() { diff --git a/common/src/main/resources/luckperms_en.properties b/common/src/main/resources/luckperms_en.properties index c587e836d..69300a4db 100644 --- a/common/src/main/resources/luckperms_en.properties +++ b/common/src/main/resources/luckperms_en.properties @@ -237,8 +237,15 @@ luckperms.command.generic.contextual-data.null-result=None luckperms.command.user.info.title=User Info luckperms.command.user.info.uuid-key=UUID luckperms.command.user.info.uuid-type-key=type -luckperms.command.user.info.uuid-type.mojang=mojang +luckperms.command.user.info.uuid-type.mojang=official luckperms.command.user.info.uuid-type.not-mojang=offline +luckperms.command.user.info.uuid-type.npc=npc +luckperms.command.user.info.uuid-type.unknown=unknown +luckperms.command.user.info.uuid-type.desc.mojang=This is a unique id for an official Minecraft: Java Edition account (online mode) +luckperms.command.user.info.uuid-type.desc.not-mojang=This is a pseudo unique id generated from the username (offline mode) +luckperms.command.user.info.uuid-type.desc.npc=This is a unique id for a NPC (non-player character) +luckperms.command.user.info.uuid-type.desc.unknown=This is an unknown unique id type +luckperms.command.user.info.uuid-type.desc.api=This is a unique id type specified via the LuckPerms API luckperms.command.user.info.status-key=Status luckperms.command.user.info.status.online=Online luckperms.command.user.info.status.offline=Offline From ae93c9768bfa24ae2330a89344b219d461dfb0c1 Mon Sep 17 00:00:00 2001 From: OmegaWeaponDev Date: Sat, 1 Jan 2022 02:32:24 +1030 Subject: [PATCH 051/314] Add default file name for export command (#3248) --- bukkit/src/main/resources/config.yml | 1 + bungee/src/main/resources/config.yml | 1 + .../common/commands/misc/ExportCommand.java | 15 +++++++++++++-- fabric/src/main/resources/luckperms.conf | 1 + nukkit/src/main/resources/config.yml | 1 + sponge/src/main/resources/luckperms.conf | 1 + velocity/src/main/resources/config.yml | 1 + 7 files changed, 19 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index cb1167ece..99c4907be 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -241,6 +241,7 @@ watch-files: true # below. # => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be # configured below. +# => custom Uses a messaging service provided using the LuckPerms API. # => auto Attempts to automatically setup a messaging service using redis or sql. messaging-service: auto diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index d90c33588..dd4addc4f 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -239,6 +239,7 @@ watch-files: true # the RedisBungee plugin installed. # => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be # configured below. +# => custom Uses a messaging service provided using the LuckPerms API. # => auto Attempts to automatically setup a messaging service using redis or sql. messaging-service: auto diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ExportCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ExportCommand.java index f9f34f87a..f8ecab689 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ExportCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ExportCommand.java @@ -38,13 +38,19 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.concurrent.atomic.AtomicBoolean; public class ExportCommand extends SingleCommand { + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm") + .withZone(ZoneId.systemDefault()); + private final AtomicBoolean running = new AtomicBoolean(false); public ExportCommand() { - super(CommandSpec.EXPORT, "Export", CommandPermission.EXPORT, Predicates.notInRange(1, 2)); + super(CommandSpec.EXPORT, "Export", CommandPermission.EXPORT, Predicates.alwaysFalse()); } @Override @@ -68,7 +74,12 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St exporter = new Exporter.WebUpload(plugin, sender, includeUsers, includeGroups, label); } else { Path dataDirectory = plugin.getBootstrap().getDataDirectory(); - Path path = dataDirectory.resolve(args.get(0) + ".json.gz"); + Path path; + if (args.isEmpty()) { + path = dataDirectory.resolve("luckperms-" + DATE_FORMAT.format(Instant.now()) + ".json.gz"); + } else { + path = dataDirectory.resolve(args.get(0) + ".json.gz"); + } if (!path.getParent().equals(dataDirectory)) { Message.FILE_NOT_WITHIN_DIRECTORY.send(sender, path.toString()); diff --git a/fabric/src/main/resources/luckperms.conf b/fabric/src/main/resources/luckperms.conf index 73c0825c0..2eb38d6eb 100644 --- a/fabric/src/main/resources/luckperms.conf +++ b/fabric/src/main/resources/luckperms.conf @@ -244,6 +244,7 @@ watch-files = true # below. # => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be # configured below. +# => custom Uses a messaging service provided using the LuckPerms API. # => auto Attempts to automatically setup a messaging service using redis or sql. messaging-service = "auto" diff --git a/nukkit/src/main/resources/config.yml b/nukkit/src/main/resources/config.yml index a20e4fe03..b61a708d9 100644 --- a/nukkit/src/main/resources/config.yml +++ b/nukkit/src/main/resources/config.yml @@ -236,6 +236,7 @@ watch-files: true # below. # => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be # configured below. +# => custom Uses a messaging service provided using the LuckPerms API. # => auto Attempts to automatically setup a messaging service using redis or sql. messaging-service: auto diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index ac04c48ba..0828a3789 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -244,6 +244,7 @@ watch-files = true # below. # => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be # configured below. +# => custom Uses a messaging service provided using the LuckPerms API. # => auto Attempts to automatically setup a messaging service using redis or sql. messaging-service = "auto" diff --git a/velocity/src/main/resources/config.yml b/velocity/src/main/resources/config.yml index e6eb8c7d8..96bad0cd3 100644 --- a/velocity/src/main/resources/config.yml +++ b/velocity/src/main/resources/config.yml @@ -230,6 +230,7 @@ watch-files: true # configured below. # => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be # configured below. +# => custom Uses a messaging service provided using the LuckPerms API. # => auto Attempts to automatically setup a messaging service using redis or sql. messaging-service: auto From 2d662cbab960c73dbbccc780a50c39203cfb0fd7 Mon Sep 17 00:00:00 2001 From: lucko Date: Fri, 31 Dec 2021 16:03:00 +0000 Subject: [PATCH 052/314] Retain cause node information for cached permission/meta data (#3250) --- .../api/query/meta/MetaValueSelector.java | 2 +- .../calculator/BukkitCalculatorFactory.java | 5 +- .../bukkit/calculator/ChildProcessor.java | 12 +- .../DefaultPermissionMapProcessor.java | 58 +++++ .../bukkit/calculator/DefaultsProcessor.java | 90 -------- .../bukkit/calculator/OpProcessor.java | 10 +- .../calculator/PermissionMapProcessor.java | 60 +++++ .../permissible/LuckPermsPermissible.java | 14 +- .../permissible/MonitoredPermissibleBase.java | 16 +- .../bukkit/vault/LuckPermsVaultChat.java | 25 +- .../vault/LuckPermsVaultPermission.java | 17 +- .../BungeePermissionCheckListener.java | 12 +- .../common/api/implementation/ApiUser.java | 4 +- .../cacheddata/AbstractCachedDataManager.java | 17 +- .../cacheddata/HolderCachedDataManager.java | 3 +- .../common/cacheddata/result/Result.java | 60 +++++ .../cacheddata/result/StringResult.java | 107 +++++++++ .../result/TristateResult.java | 103 ++++++--- .../cacheddata/type/MetaAccumulator.java | 39 ++-- .../common/cacheddata/type/MetaCache.java | 184 +++++++++++---- .../cacheddata/type/MetaStackAccumulator.java | 40 ++++ .../cacheddata/type/MonitoredMetaCache.java | 152 +++++++++++++ .../cacheddata/type/PermissionCache.java | 23 +- .../cacheddata/type/SimpleMetaCache.java | 213 ------------------ .../type/SimpleMetaValueSelector.java | 20 +- .../calculator/PermissionCalculator.java | 10 +- .../AbstractOverrideWildcardProcessor.java | 58 +++++ .../AbstractPermissionProcessor.java | 28 +-- .../AbstractSourceBasedProcessor.java | 41 ++++ .../calculator/processor/DirectProcessor.java | 8 +- .../processor/PermissionProcessor.java | 6 +- .../calculator/processor/RegexProcessor.java | 10 +- .../processor/SpongeWildcardProcessor.java | 12 +- .../processor/WildcardProcessor.java | 17 +- .../command/access/ArgumentPermissions.java | 6 +- .../generic/permission/PermissionCheck.java | 26 +-- .../common/commands/group/GroupInfo.java | 12 +- .../common/commands/user/UserInfo.java | 14 +- .../luckperms/common/locale/Message.java | 120 +++++++++- .../common/model/PermissionHolder.java | 12 +- .../model/PermissionHolderIdentifier.java | 5 + .../manager/user/AbstractUserManager.java | 4 +- .../common/node/utils/NodeJsonSerializer.java | 92 +++++--- .../luckperms/common/treeview/TreeView.java | 4 +- .../common/verbose/VerboseHandler.java | 8 +- .../common/verbose/VerboseListener.java | 112 ++++----- .../common/verbose/event/CheckOrigin.java | 63 ++++++ .../common/verbose/event/MetaCheckEvent.java | 87 ++++--- .../verbose/event/PermissionCheckEvent.java | 92 ++++---- .../common/verbose/event/VerboseEvent.java | 19 +- .../verbose/event/VerboseEventType.java | 46 ++++ .../common/webeditor/WebEditorRequest.java | 4 +- .../main/resources/luckperms_en.properties | 8 + .../fabric/FabricCalculatorFactory.java | 2 +- .../calculator/ServerOwnerProcessor.java | 11 +- .../listeners/PermissionCheckListener.java | 6 +- .../fabric/mixin/ServerPlayerEntityMixin.java | 4 +- .../nukkit/calculator/ChildProcessor.java | 12 +- .../DefaultPermissionMapProcessor.java | 58 +++++ .../nukkit/calculator/DefaultsProcessor.java | 87 ------- .../calculator/NukkitCalculatorFactory.java | 5 +- .../nukkit/calculator/OpProcessor.java | 10 +- .../calculator/PermissionMapProcessor.java | 59 +++++ .../permissible/LuckPermsPermissible.java | 14 +- .../permissible/MonitoredPermissibleBase.java | 16 +- .../proxy/api7/SubjectCollectionProxy.java | 2 +- .../sponge/service/model/LPSubject.java | 3 +- .../reference/SubjectReferenceFactory.java | 2 +- .../sponge/calculator/DefaultsProcessor.java | 91 -------- ...r.java => FixedTypeDefaultsProcessor.java} | 4 +- ...r.java => GroupTypeDefaultsProcessor.java} | 4 +- .../calculator/RootDefaultsProcessor.java | 57 +++++ .../calculator/SpongeCalculatorFactory.java | 5 +- .../calculator/TypeDefaultsProcessor.java | 60 +++++ ...or.java => UserTypeDefaultsProcessor.java} | 4 +- .../luckperms/sponge/commands/ParentAdd.java | 2 +- .../sponge/commands/ParentRemove.java | 2 +- .../sponge/commands/SpongeParentCommand.java | 2 +- .../model/calculated/CalculatedSubject.java | 40 ++-- .../CalculatedSubjectCachedDataManager.java | 13 +- .../calculated/CalculatedSubjectData.java | 53 ++++- .../model/permissionholder/GroupSubject.java | 6 +- .../PermissionHolderSubject.java | 11 +- .../model/permissionholder/UserSubject.java | 6 +- .../model/persisted/PersistedSubject.java | 8 +- .../model/persisted/SubjectStorage.java | 2 +- .../MonitoringPermissionCheckListener.java | 6 +- .../service/PlayerPermissionProvider.java | 4 +- 88 files changed, 1804 insertions(+), 1077 deletions(-) create mode 100644 bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/DefaultPermissionMapProcessor.java delete mode 100644 bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/DefaultsProcessor.java create mode 100644 bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/PermissionMapProcessor.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/cacheddata/result/Result.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java rename common/src/main/java/me/lucko/luckperms/common/{calculator => cacheddata}/result/TristateResult.java (50%) create mode 100644 common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java delete mode 100644 common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaCache.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractOverrideWildcardProcessor.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractSourceBasedProcessor.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/verbose/event/CheckOrigin.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEventType.java create mode 100644 nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/DefaultPermissionMapProcessor.java delete mode 100644 nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/DefaultsProcessor.java create mode 100644 nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/PermissionMapProcessor.java delete mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/calculator/DefaultsProcessor.java rename sponge/src/main/java/me/lucko/luckperms/sponge/calculator/{FixedDefaultsProcessor.java => FixedTypeDefaultsProcessor.java} (88%) rename sponge/src/main/java/me/lucko/luckperms/sponge/calculator/{GroupDefaultsProcessor.java => GroupTypeDefaultsProcessor.java} (88%) create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/calculator/RootDefaultsProcessor.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/calculator/TypeDefaultsProcessor.java rename sponge/src/main/java/me/lucko/luckperms/sponge/calculator/{UserDefaultsProcessor.java => UserTypeDefaultsProcessor.java} (88%) diff --git a/api/src/main/java/net/luckperms/api/query/meta/MetaValueSelector.java b/api/src/main/java/net/luckperms/api/query/meta/MetaValueSelector.java index ceb2b46a2..a73eac171 100644 --- a/api/src/main/java/net/luckperms/api/query/meta/MetaValueSelector.java +++ b/api/src/main/java/net/luckperms/api/query/meta/MetaValueSelector.java @@ -54,6 +54,6 @@ public interface MetaValueSelector { * @param values the values, in the order in which they were accumulated. * @return the selected value */ - @NonNull String selectValue(@NonNull String key, @NonNull List values); + @NonNull MetaNode selectValue(@NonNull String key, @NonNull List values); } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/BukkitCalculatorFactory.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/BukkitCalculatorFactory.java index 5233544dd..143bde77d 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/BukkitCalculatorFactory.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/BukkitCalculatorFactory.java @@ -52,7 +52,7 @@ public BukkitCalculatorFactory(LPBukkitPlugin plugin) { @Override public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metadata) { - List processors = new ArrayList<>(7); + List processors = new ArrayList<>(8); processors.add(new DirectProcessor()); @@ -75,7 +75,8 @@ public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metad boolean op = queryOptions.option(BukkitContextManager.OP_OPTION).orElse(false); if (metadata.getHolderType() == HolderType.USER && this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) { boolean overrideWildcards = this.plugin.getConfiguration().get(ConfigKeys.APPLY_DEFAULT_NEGATIONS_BEFORE_WILDCARDS); - processors.add(new DefaultsProcessor(this.plugin, overrideWildcards, op)); + processors.add(new DefaultPermissionMapProcessor(this.plugin, op)); + processors.add(new PermissionMapProcessor(this.plugin, overrideWildcards, op)); } if (op) { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/ChildProcessor.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/ChildProcessor.java index 8ee84610b..22422fedd 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/ChildProcessor.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/ChildProcessor.java @@ -26,9 +26,9 @@ package me.lucko.luckperms.bukkit.calculator; import me.lucko.luckperms.bukkit.LPBukkitPlugin; -import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractSourceBasedProcessor; import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; import net.luckperms.api.util.Tristate; @@ -40,7 +40,7 @@ /** * Permission Processor for Bukkits "child" permission system. */ -public class ChildProcessor extends AbstractPermissionProcessor implements PermissionProcessor { +public class ChildProcessor extends AbstractSourceBasedProcessor implements PermissionProcessor { private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(ChildProcessor.class); private final LPBukkitPlugin plugin; @@ -62,10 +62,10 @@ public TristateResult hasPermission(String permission) { @Override public void refresh() { Map childPermissions = new HashMap<>(); - this.sourceMap.forEach((key, value) -> { - Map children = this.plugin.getPermissionMap().getChildPermissions(key, value); + this.sourceMap.forEach((key, node) -> { + Map children = this.plugin.getPermissionMap().getChildPermissions(key, node.getValue()); children.forEach((childKey, childValue) -> { - childPermissions.put(childKey, RESULT_FACTORY.result(Tristate.of(childValue), "parent: " + key)); + childPermissions.put(childKey, RESULT_FACTORY.resultWithOverride(node, Tristate.of(childValue))); }); }); this.childPermissions = childPermissions; diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/DefaultPermissionMapProcessor.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/DefaultPermissionMapProcessor.java new file mode 100644 index 000000000..9144500ac --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/DefaultPermissionMapProcessor.java @@ -0,0 +1,58 @@ +/* + * 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.calculator; + +import me.lucko.luckperms.bukkit.LPBukkitPlugin; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor; +import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; + +import net.luckperms.api.util.Tristate; + +/** + * Permission Processor for Bukkits "default" permission system. + */ +public class DefaultPermissionMapProcessor extends AbstractPermissionProcessor implements PermissionProcessor { + private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(DefaultPermissionMapProcessor.class); + + private final LPBukkitPlugin plugin; + private final boolean isOp; + + public DefaultPermissionMapProcessor(LPBukkitPlugin plugin, boolean isOp) { + this.plugin = plugin; + this.isOp = isOp; + } + + @Override + public TristateResult hasPermission(String permission) { + Tristate t = this.plugin.getDefaultPermissionMap().lookupDefaultPermission(permission, this.isOp); + if (t != Tristate.UNDEFINED) { + return RESULT_FACTORY.result(t); + } + + return TristateResult.UNDEFINED; + } +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/DefaultsProcessor.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/DefaultsProcessor.java deleted file mode 100644 index 4f0a6a846..000000000 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/DefaultsProcessor.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.calculator; - -import me.lucko.luckperms.bukkit.LPBukkitPlugin; -import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import me.lucko.luckperms.common.calculator.processor.SpongeWildcardProcessor; -import me.lucko.luckperms.common.calculator.processor.WildcardProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; - -import net.luckperms.api.util.Tristate; - -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; - -/** - * Permission Processor for Bukkits "default" permission system. - */ -public class DefaultsProcessor implements PermissionProcessor { - private static final TristateResult.Factory DEFAULT_PERMISSION_MAP_RESULT_FACTORY = new TristateResult.Factory(DefaultsProcessor.class, "default permission map"); - private static final TristateResult.Factory PERMISSION_MAP_RESULT_FACTORY = new TristateResult.Factory(DefaultsProcessor.class, "permission map"); - - private final LPBukkitPlugin plugin; - private final boolean overrideWildcards; - private final boolean isOp; - - public DefaultsProcessor(LPBukkitPlugin plugin, boolean overrideWildcards, boolean isOp) { - this.plugin = plugin; - this.overrideWildcards = overrideWildcards; - this.isOp = isOp; - } - - private boolean canOverrideWildcard(TristateResult prev) { - return this.overrideWildcards && - (prev.processorClass() == WildcardProcessor.class || prev.processorClass() == SpongeWildcardProcessor.class) && - prev.result() == Tristate.TRUE; - } - - @Override - public TristateResult hasPermission(TristateResult prev, String permission) { - if (prev != TristateResult.UNDEFINED) { - // Check to see if the result should be overridden - if (canOverrideWildcard(prev)) { - Permission defPerm = this.plugin.getPermissionMap().get(permission); - if (defPerm != null) { - PermissionDefault def = defPerm.getDefault(); - if (def == PermissionDefault.FALSE || this.isOp && def == PermissionDefault.NOT_OP) { - return PERMISSION_MAP_RESULT_FACTORY.result(Tristate.FALSE, "permission map (overriding wildcard): " + prev.cause()); - } - } - } - - return prev; - } - - Tristate t = this.plugin.getDefaultPermissionMap().lookupDefaultPermission(permission, this.isOp); - if (t != Tristate.UNDEFINED) { - return DEFAULT_PERMISSION_MAP_RESULT_FACTORY.result(t); - } - - Permission defPerm = this.plugin.getPermissionMap().get(permission); - if (defPerm == null) { - return TristateResult.UNDEFINED; - } - return PERMISSION_MAP_RESULT_FACTORY.result(Tristate.of(defPerm.getDefault().getValue(this.isOp))); - } -} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/OpProcessor.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/OpProcessor.java index a4b06a18d..c552dd5cf 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/OpProcessor.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/OpProcessor.java @@ -25,8 +25,9 @@ package me.lucko.luckperms.bukkit.calculator; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor; import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; import net.luckperms.api.util.Tristate; @@ -34,7 +35,7 @@ * Permission Processor which is added for opped users, to simply return true if * no other processors match. */ -public final class OpProcessor implements PermissionProcessor { +public final class OpProcessor extends AbstractPermissionProcessor implements PermissionProcessor { private static final TristateResult TRUE_RESULT = new TristateResult.Factory(OpProcessor.class).result(Tristate.TRUE); public static final OpProcessor INSTANCE = new OpProcessor(); @@ -44,10 +45,7 @@ private OpProcessor() { } @Override - public TristateResult hasPermission(TristateResult prev, String permission) { - if (prev != TristateResult.UNDEFINED) { - return prev; - } + public TristateResult hasPermission(String permission) { return TRUE_RESULT; } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/PermissionMapProcessor.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/PermissionMapProcessor.java new file mode 100644 index 000000000..4f6ac96c5 --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculator/PermissionMapProcessor.java @@ -0,0 +1,60 @@ +/* + * 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.calculator; + +import me.lucko.luckperms.bukkit.LPBukkitPlugin; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractOverrideWildcardProcessor; +import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; + +import net.luckperms.api.util.Tristate; + +import org.bukkit.permissions.Permission; + +/** + * Permission Processor for Bukkits "default" permission system. + */ +public class PermissionMapProcessor extends AbstractOverrideWildcardProcessor implements PermissionProcessor { + private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(PermissionMapProcessor.class); + + private final LPBukkitPlugin plugin; + private final boolean isOp; + + public PermissionMapProcessor(LPBukkitPlugin plugin, boolean overrideWildcards, boolean isOp) { + super(overrideWildcards); + this.plugin = plugin; + this.isOp = isOp; + } + + @Override + public TristateResult hasPermission(String permission) { + Permission defPerm = this.plugin.getPermissionMap().get(permission); + if (defPerm == null) { + return TristateResult.UNDEFINED; + } + return RESULT_FACTORY.result(Tristate.of(defPerm.getDefault().getValue(this.isOp))); + } +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissible.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissible.java index 164b8d658..3b4c7bc0d 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissible.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/LuckPermsPermissible.java @@ -29,13 +29,13 @@ import com.google.common.collect.ImmutableSet; import me.lucko.luckperms.bukkit.LPBukkitPlugin; -import me.lucko.luckperms.bukkit.calculator.DefaultsProcessor; import me.lucko.luckperms.bukkit.calculator.OpProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.bukkit.calculator.PermissionMapProcessor; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.model.User; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; @@ -143,13 +143,13 @@ public boolean isPermissionSet(@NonNull String permission) { } QueryOptions queryOptions = this.queryOptionsSupplier.getQueryOptions(); - TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK); + TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET); if (result.result() == Tristate.UNDEFINED) { return false; } // ignore matches made from looking up in the permission map (replicate bukkit behaviour) - if (result.processorClass() == DefaultsProcessor.class && "permission map".equals(result.cause())) { + if (result.processorClass() == PermissionMapProcessor.class) { return false; } @@ -173,7 +173,7 @@ public boolean hasPermission(@NonNull String permission) { } QueryOptions queryOptions = this.queryOptionsSupplier.getQueryOptions(); - return this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK).result().asBoolean(); + return this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result().asBoolean(); } @Override @@ -183,7 +183,7 @@ public boolean hasPermission(@NonNull Permission permission) { } QueryOptions queryOptions = this.queryOptionsSupplier.getQueryOptions(); - TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission.getName(), PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK); + TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission.getName(), CheckOrigin.PLATFORM_API_HAS_PERMISSION); // override default op handling using the Permission class we have if (result.processorClass() == OpProcessor.class && this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/MonitoredPermissibleBase.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/MonitoredPermissibleBase.java index e3c4f57c5..45258c93e 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/MonitoredPermissibleBase.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/inject/permissible/MonitoredPermissibleBase.java @@ -25,12 +25,12 @@ package me.lucko.luckperms.bukkit.inject.permissible; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; import me.lucko.luckperms.common.verbose.VerboseHandler; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.util.Tristate; @@ -69,8 +69,8 @@ public MonitoredPermissibleBase(LuckPermsPlugin plugin, PermissibleBase delegate this.initialised = true; } - private void logCheck(PermissionCheckEvent.Origin origin, String permission, boolean result) { - this.plugin.getVerboseHandler().offerPermissionCheckEvent(origin, this.verboseCheckTarget, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.of(Tristate.of(result))); + private void logCheck(CheckOrigin origin, String permission, boolean result) { + this.plugin.getVerboseHandler().offerPermissionCheckEvent(origin, this.verboseCheckTarget, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.forMonitoredResult(Tristate.of(result))); this.plugin.getPermissionRegistry().offer(permission); } @@ -85,7 +85,7 @@ public boolean isPermissionSet(@NonNull String permission) { } final boolean result = this.delegate.isPermissionSet(permission); - logCheck(PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK, permission, result); + logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET, permission, result); return result; } @@ -96,7 +96,7 @@ public boolean isPermissionSet(@NonNull Permission permission) { } final boolean result = this.delegate.isPermissionSet(permission); - logCheck(PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK, permission.getName(), result); + logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET, permission.getName(), result); return result; } @@ -107,7 +107,7 @@ public boolean hasPermission(@NonNull String permission) { } final boolean result = this.delegate.hasPermission(permission); - logCheck(PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK, permission, result); + logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION, permission, result); return result; } @@ -118,7 +118,7 @@ public boolean hasPermission(@NonNull Permission permission) { } final boolean result = this.delegate.hasPermission(permission); - logCheck(PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK, permission.getName(), result); + logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION, permission.getName(), result); return result; } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultChat.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultChat.java index c77d3016c..cc5e80150 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultChat.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultChat.java @@ -30,6 +30,7 @@ import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator; import me.lucko.luckperms.common.cacheddata.type.MetaCache; +import me.lucko.luckperms.common.cacheddata.type.MonitoredMetaCache; import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; @@ -37,7 +38,7 @@ import me.lucko.luckperms.common.node.types.Prefix; import me.lucko.luckperms.common.node.types.Suffix; import me.lucko.luckperms.common.query.QueryOptionsImpl; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.context.DefaultContextKeys; import net.luckperms.api.context.ImmutableContextSet; @@ -97,7 +98,7 @@ public String getUserChatPrefix(String world, UUID uuid) { PermissionHolder user = this.vaultPermission.lookupUser(uuid); QueryOptions queryOptions = this.vaultPermission.getQueryOptions(uuid, world); MetaCache metaData = user.getCachedData().getMetaData(queryOptions); - return Strings.nullToEmpty(metaData.getPrefix(MetaCheckEvent.Origin.THIRD_PARTY_API)); + return Strings.nullToEmpty(metaData.getPrefix(CheckOrigin.THIRD_PARTY_API).result()); } @Override @@ -107,7 +108,7 @@ public String getUserChatSuffix(String world, UUID uuid) { PermissionHolder user = this.vaultPermission.lookupUser(uuid); QueryOptions queryOptions = this.vaultPermission.getQueryOptions(uuid, world); MetaCache metaData = user.getCachedData().getMetaData(queryOptions); - return Strings.nullToEmpty(metaData.getSuffix(MetaCheckEvent.Origin.THIRD_PARTY_API)); + return Strings.nullToEmpty(metaData.getSuffix(CheckOrigin.THIRD_PARTY_API).result()); } @Override @@ -139,8 +140,8 @@ public String getUserMeta(String world, UUID uuid, String key) { PermissionHolder user = this.vaultPermission.lookupUser(uuid); QueryOptions queryOptions = this.vaultPermission.getQueryOptions(uuid, world); - MetaCache metaData = user.getCachedData().getMetaData(queryOptions); - return metaData.getMetaValue(key, MetaCheckEvent.Origin.THIRD_PARTY_API); + MonitoredMetaCache metaData = user.getCachedData().getMetaData(queryOptions); + return metaData.getMetaValue(key, CheckOrigin.THIRD_PARTY_API).result(); } @Override @@ -158,21 +159,21 @@ public void setUserMeta(String world, UUID uuid, String key, Object value) { @Override public String getGroupChatPrefix(String world, String name) { Objects.requireNonNull(name, "name"); - MetaCache metaData = getGroupMetaCache(name, world); + MonitoredMetaCache metaData = getGroupMetaCache(name, world); if (metaData == null) { return null; } - return Strings.nullToEmpty(metaData.getPrefix(MetaCheckEvent.Origin.THIRD_PARTY_API)); + return Strings.nullToEmpty(metaData.getPrefix(CheckOrigin.THIRD_PARTY_API).result()); } @Override public String getGroupChatSuffix(String world, String name) { Objects.requireNonNull(name, "name"); - MetaCache metaData = getGroupMetaCache(name, world); + MonitoredMetaCache metaData = getGroupMetaCache(name, world); if (metaData == null) { return null; } - return Strings.nullToEmpty(metaData.getSuffix(MetaCheckEvent.Origin.THIRD_PARTY_API)); + return Strings.nullToEmpty(metaData.getSuffix(CheckOrigin.THIRD_PARTY_API).result()); } @Override @@ -199,11 +200,11 @@ public void setGroupChatSuffix(String world, String name, String suffix) { public String getGroupMeta(String world, String name, String key) { Objects.requireNonNull(name, "name"); Objects.requireNonNull(key, "key"); - MetaCache metaData = getGroupMetaCache(name, world); + MonitoredMetaCache metaData = getGroupMetaCache(name, world); if (metaData == null) { return null; } - return metaData.getMetaValue(key, MetaCheckEvent.Origin.THIRD_PARTY_API); + return metaData.getMetaValue(key, CheckOrigin.THIRD_PARTY_API).result(); } @Override @@ -223,7 +224,7 @@ private Group getGroup(String name) { return this.plugin.getGroupManager().getByDisplayName(name); } - private MetaCache getGroupMetaCache(String name, String world) { + private MonitoredMetaCache getGroupMetaCache(String name, String world) { Group group = getGroup(name); if (group == null) { return null; diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java index 345fda82d..8c189c5ac 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java @@ -29,10 +29,10 @@ import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.bukkit.context.BukkitContextManager; -import me.lucko.luckperms.common.cacheddata.type.MetaCache; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.type.MonitoredMetaCache; import me.lucko.luckperms.common.cacheddata.type.PermissionCache; import me.lucko.luckperms.common.calculator.processor.DirectProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.HolderType; @@ -44,8 +44,7 @@ import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.util.UniqueIdType; import me.lucko.luckperms.common.util.Uuids; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.DefaultContextKeys; @@ -175,7 +174,7 @@ public boolean userHasPermission(String world, UUID uuid, String permission) { PermissionHolder user = lookupUser(uuid); QueryOptions queryOptions = getQueryOptions(uuid, world); PermissionCache permissionData = user.getCachedData().getPermissionData(queryOptions); - return permissionData.checkPermission(permission, PermissionCheckEvent.Origin.THIRD_PARTY_API).result().asBoolean(); + return permissionData.checkPermission(permission, CheckOrigin.THIRD_PARTY_API).result().asBoolean(); } @Override @@ -211,7 +210,7 @@ public boolean userInGroup(String world, UUID uuid, String group) { QueryOptions queryOptions = getQueryOptions(uuid, world); PermissionCache permissionData = user.getCachedData().getPermissionData(queryOptions); - TristateResult result = permissionData.checkPermission(Inheritance.key(rewriteGroupName(group)), PermissionCheckEvent.Origin.THIRD_PARTY_API); + TristateResult result = permissionData.checkPermission(Inheritance.key(rewriteGroupName(group)), CheckOrigin.THIRD_PARTY_API); return result.processorClass() == DirectProcessor.class && result.result().asBoolean(); } @@ -254,8 +253,8 @@ public String userGetPrimaryGroup(String world, UUID uuid) { } QueryOptions queryOptions = getQueryOptions(uuid, world); - MetaCache metaData = user.getCachedData().getMetaData(queryOptions); - String value = metaData.getPrimaryGroup(MetaCheckEvent.Origin.THIRD_PARTY_API); + MonitoredMetaCache metaData = user.getCachedData().getMetaData(queryOptions); + String value = metaData.getPrimaryGroup(CheckOrigin.THIRD_PARTY_API); if (value == null) { return null; } @@ -276,7 +275,7 @@ public boolean groupHasPermission(String world, String name, String permission) QueryOptions queryOptions = getQueryOptions(null, world); PermissionCache permissionData = group.getCachedData().getPermissionData(queryOptions); - return permissionData.checkPermission(permission, PermissionCheckEvent.Origin.THIRD_PARTY_API).result().asBoolean(); + return permissionData.checkPermission(permission, CheckOrigin.THIRD_PARTY_API).result().asBoolean(); } @Override diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/listeners/BungeePermissionCheckListener.java b/bungee/src/main/java/me/lucko/luckperms/bungee/listeners/BungeePermissionCheckListener.java index 18ddad379..e0d0119bb 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/listeners/BungeePermissionCheckListener.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/listeners/BungeePermissionCheckListener.java @@ -27,12 +27,12 @@ import me.lucko.luckperms.bungee.LPBungeePlugin; import me.lucko.luckperms.bungee.event.TristateCheckEvent; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent.Origin; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; @@ -72,7 +72,7 @@ public void onPlayerPermissionCheck(PermissionCheckEvent e) { } QueryOptions queryOptions = this.plugin.getContextManager().getQueryOptions(player); - Tristate result = user.getCachedData().getPermissionData(queryOptions).checkPermission(e.getPermission(), Origin.PLATFORM_PERMISSION_CHECK).result(); + Tristate result = user.getCachedData().getPermissionData(queryOptions).checkPermission(e.getPermission(), CheckOrigin.PLATFORM_API_HAS_PERMISSION).result(); if (result == Tristate.UNDEFINED && this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) { return; // just use the result provided by the proxy when the event was created } @@ -101,7 +101,7 @@ public void onPlayerTristateCheck(TristateCheckEvent e) { } QueryOptions queryOptions = this.plugin.getContextManager().getQueryOptions(player); - Tristate result = user.getCachedData().getPermissionData(queryOptions).checkPermission(e.getPermission(), Origin.PLATFORM_LOOKUP_CHECK).result(); + Tristate result = user.getCachedData().getPermissionData(queryOptions).checkPermission(e.getPermission(), CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET).result(); if (result == Tristate.UNDEFINED && this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) { return; // just use the result provided by the proxy when the event was created } @@ -122,7 +122,7 @@ public void onOtherPermissionCheck(PermissionCheckEvent e) { Tristate result = Tristate.of(e.hasPermission()); VerboseCheckTarget target = VerboseCheckTarget.internal(e.getSender().getName()); - this.plugin.getVerboseHandler().offerPermissionCheckEvent(Origin.PLATFORM_PERMISSION_CHECK, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.of(result)); + this.plugin.getVerboseHandler().offerPermissionCheckEvent(CheckOrigin.PLATFORM_API_HAS_PERMISSION, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.forMonitoredResult(result)); this.plugin.getPermissionRegistry().offer(permission); } @@ -139,7 +139,7 @@ public void onOtherTristateCheck(TristateCheckEvent e) { Tristate result = e.getResult(); VerboseCheckTarget target = VerboseCheckTarget.internal(e.getSender().getName()); - this.plugin.getVerboseHandler().offerPermissionCheckEvent(Origin.PLATFORM_LOOKUP_CHECK, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.of(result)); + this.plugin.getVerboseHandler().offerPermissionCheckEvent(CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.forMonitoredResult(result)); this.plugin.getPermissionRegistry().offer(permission); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiUser.java b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiUser.java index 113ce698b..52e8c8961 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiUser.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiUser.java @@ -30,7 +30,7 @@ import me.lucko.luckperms.common.cacheddata.UserCachedDataManager; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.types.Inheritance; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.model.data.DataMutateResult; import net.luckperms.api.model.data.DataType; @@ -72,7 +72,7 @@ public String getUsername() { @Override public @NonNull String getPrimaryGroup() { - String value = this.handle.getCachedData().getMetaData(this.handle.getQueryOptions()).getPrimaryGroup(MetaCheckEvent.Origin.LUCKPERMS_API); + String value = this.handle.getCachedData().getMetaData(this.handle.getQueryOptions()).getPrimaryGroup(CheckOrigin.LUCKPERMS_API); Objects.requireNonNull(value, "value"); // assert nonnull return value; } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java index ccf2a0c4a..581c11663 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java @@ -27,7 +27,7 @@ import me.lucko.luckperms.common.cache.LoadingMap; import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator; -import me.lucko.luckperms.common.cacheddata.type.MetaCache; +import me.lucko.luckperms.common.cacheddata.type.MonitoredMetaCache; import me.lucko.luckperms.common.cacheddata.type.PermissionCache; import me.lucko.luckperms.common.calculator.CalculatorFactory; import me.lucko.luckperms.common.calculator.PermissionCalculator; @@ -40,6 +40,7 @@ import net.luckperms.api.cacheddata.CachedPermissionData; import net.luckperms.api.metastacking.MetaStackDefinition; import net.luckperms.api.node.ChatMetaType; +import net.luckperms.api.node.Node; import net.luckperms.api.query.QueryOptions; import org.checkerframework.checker.nullness.qual.NonNull; @@ -59,7 +60,7 @@ public abstract class AbstractCachedDataManager implements CachedDataManager { private final LuckPermsPlugin plugin; private final AbstractContainer permission; - private final AbstractContainer meta; + private final AbstractContainer meta; protected AbstractCachedDataManager(LuckPermsPlugin plugin) { this.plugin = plugin; @@ -87,7 +88,7 @@ public LuckPermsPlugin getPlugin() { } @Override - public @NonNull MetaCache getMetaData(@NonNull QueryOptions queryOptions) { + public @NonNull MonitoredMetaCache getMetaData(@NonNull QueryOptions queryOptions) { return this.meta.get(queryOptions); } @@ -97,7 +98,7 @@ public LuckPermsPlugin getPlugin() { } @Override - public @NonNull MetaCache getMetaData() { + public @NonNull MonitoredMetaCache getMetaData() { return getMetaData(getQueryOptions()); } @@ -139,7 +140,7 @@ public LuckPermsPlugin getPlugin() { * @param the map type * @return the resolved permissions */ - protected abstract > M resolvePermissions(IntFunction mapFactory, QueryOptions queryOptions); + protected abstract > M resolvePermissions(IntFunction mapFactory, QueryOptions queryOptions); /** * Resolves the owners meta data for the given {@link QueryOptions}. @@ -153,18 +154,18 @@ private PermissionCache calculatePermissions(QueryOptions queryOptions) { Objects.requireNonNull(queryOptions, "queryOptions"); CacheMetadata metadata = getMetadataForQueryOptions(queryOptions); - ConcurrentHashMap sourcePermissions = resolvePermissions(ConcurrentHashMap::new, queryOptions); + ConcurrentHashMap sourcePermissions = resolvePermissions(ConcurrentHashMap::new, queryOptions); return new PermissionCache(queryOptions, metadata, getCalculatorFactory(), sourcePermissions); } - private MetaCache calculateMeta(QueryOptions queryOptions) { + private MonitoredMetaCache calculateMeta(QueryOptions queryOptions) { Objects.requireNonNull(queryOptions, "queryOptions"); CacheMetadata metadata = getMetadataForQueryOptions(queryOptions); MetaAccumulator accumulator = newAccumulator(queryOptions); resolveMeta(accumulator, queryOptions); - return new MetaCache(this.plugin, queryOptions, metadata, accumulator); + return new MonitoredMetaCache(this.plugin, queryOptions, metadata, accumulator); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/HolderCachedDataManager.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/HolderCachedDataManager.java index 6938e7651..ca48561ac 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/HolderCachedDataManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/HolderCachedDataManager.java @@ -32,6 +32,7 @@ import net.luckperms.api.metastacking.MetaStackDefinition; import net.luckperms.api.node.ChatMetaType; +import net.luckperms.api.node.Node; import net.luckperms.api.query.QueryOptions; import java.util.Map; @@ -75,7 +76,7 @@ protected MetaStackDefinition getDefaultMetaStackDefinition(ChatMetaType type) { } @Override - protected > M resolvePermissions(IntFunction mapFactory, QueryOptions queryOptions) { + protected > M resolvePermissions(IntFunction mapFactory, QueryOptions queryOptions) { return this.holder.exportPermissions(mapFactory, queryOptions, true, getPlugin().getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND)); } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/Result.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/Result.java new file mode 100644 index 000000000..be4294eea --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/Result.java @@ -0,0 +1,60 @@ +/* + * 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.cacheddata.result; + +import net.luckperms.api.node.Node; + +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Represents the result of a cached data lookup + * + * @param the result type + */ +public interface Result { + + /** + * Gets the underlying result. + * + * @return the underlying result + */ + T result(); + + /** + * Gets the node that caused the result. + * + * @return the causing node + */ + @Nullable N node(); + + /** + * Gets the result that this result overrides, if applicable. + * + * @return the overridden result + */ + @Nullable Result overriddenResult(); + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java new file mode 100644 index 000000000..8efaff7f5 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java @@ -0,0 +1,107 @@ +/* + * 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.cacheddata.result; + +import net.luckperms.api.node.Node; +import net.luckperms.api.node.types.ChatMetaNode; +import net.luckperms.api.node.types.MetaNode; + +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Represents the result of a meta lookup + * + * @param the node type + */ +public final class StringResult implements Result { + + /** The result, nullable */ + private final String result; + /** The node that caused the result */ + private final N node; + /** A reference to another result that this one overrides */ + private StringResult overriddenResult; + + public StringResult(String result, N node, StringResult overriddenResult) { + this.result = result; + this.node = node; + this.overriddenResult = overriddenResult; + } + + @Override + public @Nullable String result() { + return this.result; + } + + @Override + public @Nullable N node() { + return this.node; + } + + @Override + public @Nullable StringResult overriddenResult() { + return this.overriddenResult; + } + + public void setOverriddenResult(StringResult overriddenResult) { + this.overriddenResult = overriddenResult; + } + + public StringResult copy() { + return new StringResult<>(this.result, this.node, this.overriddenResult); + } + + @Override + public String toString() { + return "StringResult(" + + "result=" + this.result + ", " + + "node=" + this.node + ", " + + "overriddenResult=" + this.overriddenResult + ')'; + } + + private static final StringResult NULL_RESULT = new StringResult<>(null, null, null); + + @SuppressWarnings("unchecked") + public static StringResult nullResult() { + return (StringResult) NULL_RESULT; + } + + public static StringResult of(String result) { + return new StringResult<>(result, null, null); + } + + public static StringResult of(MetaNode node) { + return new StringResult<>(node.getMetaValue(), node, null); + } + + public static StringResult> of(ChatMetaNode node) { + return new StringResult<>(node.getMetaValue(), node, null); + } + + public static StringResult> of(String result, ChatMetaNode node) { + return new StringResult<>(result, node, null); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/result/TristateResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java similarity index 50% rename from common/src/main/java/me/lucko/luckperms/common/calculator/result/TristateResult.java rename to common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java index 931b801a7..bbdb0dd97 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculator/result/TristateResult.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java @@ -23,78 +23,110 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.calculator.result; +package me.lucko.luckperms.common.cacheddata.result; import me.lucko.luckperms.common.calculator.PermissionCalculator; import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; +import net.luckperms.api.node.Node; import net.luckperms.api.util.Tristate; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Represents the result of a {@link PermissionCalculator} lookup. */ -public final class TristateResult { - - private static final Factory NULL_FACTORY = new Factory(null, null); - public static final TristateResult UNDEFINED = new TristateResult(Tristate.UNDEFINED, null, null); +public final class TristateResult implements Result { - public static TristateResult of(Tristate result) { - return NULL_FACTORY.result(result); - } - + /** The result */ private final Tristate result; + /** The node that caused the result */ + private final Node node; + /** The permission processor that provided the result */ private final Class processorClass; - private final String cause; + /** A reference to another result that this one overrides */ + private TristateResult overriddenResult; - private TristateResult(Tristate result, Class processorClass, String cause) { + private TristateResult(Tristate result, Node node, Class processorClass) { this.result = result; + this.node = node; this.processorClass = processorClass; - this.cause = cause; } - public Tristate result() { + @Override + public @NonNull Tristate result() { return this.result; } - public Class processorClass() { + @Override + public @Nullable Node node() { + return this.node; + } + + public @Nullable Class processorClass() { return this.processorClass; } - public String cause() { - return this.cause; + public @Nullable String processorClassFriendly() { + if (this.processorClass == null) { + return null; + } else if (this.processorClass.getName().startsWith("me.lucko.luckperms.")) { + String simpleName = this.processorClass.getSimpleName(); + String platform = this.processorClass.getName().split("\\.")[3]; + return platform + "." + simpleName; + } else { + return this.processorClass.getName(); + } + } + + @Override + public @Nullable TristateResult overriddenResult() { + return this.overriddenResult; + } + + public void setOverriddenResult(TristateResult overriddenResult) { + this.overriddenResult = overriddenResult; } @Override public String toString() { return "TristateResult(" + "result=" + this.result + ", " + + "node=" + this.node + ", " + "processorClass=" + this.processorClass + ", " + - "cause=" + this.cause + ')'; + "overriddenResult=" + this.overriddenResult + ')'; + } + + private static final TristateResult TRUE = new TristateResult(Tristate.TRUE, null,null); + private static final TristateResult FALSE = new TristateResult(Tristate.FALSE, null,null); + public static final TristateResult UNDEFINED = new TristateResult(Tristate.UNDEFINED, null,null); + + public static TristateResult forMonitoredResult(Tristate result) { + switch (result) { + case TRUE: + return TRUE; + case FALSE: + return FALSE; + case UNDEFINED: + return UNDEFINED; + default: + throw new AssertionError(); + } } public static final class Factory { private final Class processorClass; - private final TristateResult trueResult; - private final TristateResult falseResult; - - public Factory(Class processorClass, String defaultCause) { - this.processorClass = processorClass; - - this.trueResult = new TristateResult(Tristate.TRUE, processorClass, defaultCause); - this.falseResult = new TristateResult(Tristate.FALSE, processorClass, defaultCause); - } - public Factory(Class processorClass) { - this(processorClass, null); + this.processorClass = processorClass; } public TristateResult result(Tristate result) { switch (result) { case TRUE: - return this.trueResult; case FALSE: - return this.falseResult; + return new TristateResult(result, null, this.processorClass); case UNDEFINED: return UNDEFINED; default: @@ -102,11 +134,12 @@ public TristateResult result(Tristate result) { } } - public TristateResult result(Tristate result, String cause) { - if (result == Tristate.UNDEFINED) { - return UNDEFINED; - } - return new TristateResult(result, this.processorClass, cause); + public TristateResult result(@NonNull Node node) { + return new TristateResult(Tristate.of(node.getValue()), node, this.processorClass); + } + + public TristateResult resultWithOverride(@NonNull Node node, Tristate result) { + return new TristateResult(result, node, this.processorClass); } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java index fd6dfa8b9..2252387c8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java @@ -28,6 +28,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import me.lucko.luckperms.common.cacheddata.result.StringResult; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.node.types.Weight; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; @@ -35,6 +36,7 @@ import net.luckperms.api.metastacking.MetaStackDefinition; import net.luckperms.api.node.ChatMetaType; import net.luckperms.api.node.Node; +import net.luckperms.api.node.types.ChatMetaNode; import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.node.types.PrefixNode; import net.luckperms.api.node.types.SuffixNode; @@ -75,9 +77,9 @@ private enum State { private final AtomicReference state = new AtomicReference<>(State.ACCUMULATING); - private final ListMultimap meta; - private final SortedMap prefixes; - private final SortedMap suffixes; + private final ListMultimap> meta; + private final SortedMap>> prefixes; + private final SortedMap>> suffixes; private int weight = 0; private String primaryGroup; @@ -119,10 +121,10 @@ public void complete() { // perform final changes if (!this.meta.containsKey(Weight.NODE_KEY) && this.weight != 0) { - this.meta.put(Weight.NODE_KEY, String.valueOf(this.weight)); + this.meta.put(Weight.NODE_KEY, StringResult.of(String.valueOf(this.weight))); } if (this.primaryGroup != null && !this.meta.containsKey("primarygroup")) { - this.meta.put("primarygroup", this.primaryGroup); + this.meta.put("primarygroup", StringResult.of(this.primaryGroup)); } this.seenNodeKeys = null; // free up for GC @@ -146,27 +148,22 @@ public void accumulateNode(Node n) { if (n instanceof MetaNode) { MetaNode mn = (MetaNode) n; - this.meta.put(mn.getMetaKey(), mn.getMetaValue()); + this.meta.put(mn.getMetaKey(), StringResult.of(mn)); } if (n instanceof PrefixNode) { PrefixNode pn = (PrefixNode) n; - this.prefixes.putIfAbsent(pn.getPriority(), pn.getMetaValue()); + this.prefixes.putIfAbsent(pn.getPriority(), StringResult.of(pn)); this.prefixAccumulator.offer(pn); } if (n instanceof SuffixNode) { SuffixNode pn = (SuffixNode) n; - this.suffixes.putIfAbsent(pn.getPriority(), pn.getMetaValue()); + this.suffixes.putIfAbsent(pn.getPriority(), StringResult.of(pn)); this.suffixAccumulator.offer(pn); } } - public void accumulateMeta(String key, String value) { - ensureState(State.ACCUMULATING); - this.meta.put(key, value); - } - public void accumulateWeight(int weight) { ensureState(State.ACCUMULATING); this.weight = Math.max(this.weight, weight); @@ -179,22 +176,22 @@ public void setPrimaryGroup(String primaryGroup) { // read methods - public ListMultimap getMeta() { + public ListMultimap> getMeta() { ensureState(State.COMPLETE); return this.meta; } - public Map getChatMeta(ChatMetaType type) { + public Map>> getChatMeta(ChatMetaType type) { ensureState(State.COMPLETE); return type == ChatMetaType.PREFIX ? this.prefixes : this.suffixes; } - public SortedMap getPrefixes() { + public SortedMap>> getPrefixes() { ensureState(State.COMPLETE); return this.prefixes; } - public SortedMap getSuffixes() { + public SortedMap>> getSuffixes() { ensureState(State.COMPLETE); return this.suffixes; } @@ -219,14 +216,14 @@ public MetaStackDefinition getSuffixDefinition() { return this.suffixDefinition; } - public String getPrefix() { + public StringResult> getPrefix() { ensureState(State.COMPLETE); - return this.prefixAccumulator.toFormattedString(); + return this.prefixAccumulator.toResult(); } - public String getSuffix() { + public StringResult> getSuffix() { ensureState(State.COMPLETE); - return this.suffixAccumulator.toFormattedString(); + return this.suffixAccumulator.toResult(); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java index f65b7678a..cfde15f96 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java @@ -26,102 +26,196 @@ package me.lucko.luckperms.common.cacheddata.type; import com.google.common.collect.ForwardingMap; - -import me.lucko.luckperms.common.cacheddata.CacheMetadata; -import me.lucko.luckperms.common.node.types.Prefix; -import me.lucko.luckperms.common.node.types.Suffix; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimaps; + +import me.lucko.luckperms.common.cacheddata.UsageTracked; +import me.lucko.luckperms.common.cacheddata.result.StringResult; +import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.cacheddata.CachedMetaData; +import net.luckperms.api.metastacking.MetaStackDefinition; +import net.luckperms.api.node.types.ChatMetaNode; +import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.query.QueryOptions; +import net.luckperms.api.query.meta.MetaValueSelector; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; /** * Holds cached meta for a given context */ -public class MetaCache extends SimpleMetaCache implements CachedMetaData { +public class MetaCache extends UsageTracked implements CachedMetaData { - /** The plugin instance */ private final LuckPermsPlugin plugin; - /** The metadata for this cache */ - private final CacheMetadata metadata; - - public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, CacheMetadata metadata, MetaAccumulator sourceMeta) { - super(plugin, queryOptions, sourceMeta); + /** The query options this container is holding data for */ + private final QueryOptions queryOptions; + + /* The data */ + private final Map>> meta; + private final Map> flattenedMeta; + private final SortedMap>> prefixes; + private final SortedMap>> suffixes; + private final int weight; + private final String primaryGroup; + private final MetaStackDefinition prefixDefinition; + private final MetaStackDefinition suffixDefinition; + private final StringResult> prefix; + private final StringResult> suffix; + + public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, MetaAccumulator sourceMeta) { this.plugin = plugin; - this.metadata = metadata; + this.queryOptions = queryOptions; + + Map>> meta = Multimaps.asMap(ImmutableListMultimap.copyOf(sourceMeta.getMeta())); + + MetaValueSelector metaValueSelector = this.queryOptions.option(MetaValueSelector.KEY) + .orElseGet(() -> this.plugin.getConfiguration().get(ConfigKeys.META_VALUE_SELECTOR)); + + ImmutableMap.Builder> builder = ImmutableMap.builder(); + for (Map.Entry>> e : meta.entrySet()) { + if (e.getValue().isEmpty()) { + continue; + } + + MetaNode selected = metaValueSelector.selectValue(e.getKey(), Lists.transform(e.getValue(), StringResult::node)); + if (selected == null) { + throw new NullPointerException(metaValueSelector + " returned null"); + } + + builder.put(e.getKey(), StringResult.of(selected)); + } + this.flattenedMeta = builder.build(); + this.meta = new LowerCaseMetaMap(meta); + + this.prefixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getPrefixes()); + this.suffixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getSuffixes()); + this.weight = sourceMeta.getWeight(); + this.primaryGroup = sourceMeta.getPrimaryGroup(); + this.prefixDefinition = sourceMeta.getPrefixDefinition(); + this.suffixDefinition = sourceMeta.getSuffixDefinition(); + this.prefix = sourceMeta.getPrefix(); + this.suffix = sourceMeta.getSuffix(); + } + + public @NonNull StringResult getMetaValue(String key, CheckOrigin origin) { + Objects.requireNonNull(key, "key"); + return this.flattenedMeta.getOrDefault(key.toLowerCase(Locale.ROOT), StringResult.nullResult()); + } + + @Override + public final @Nullable String getMetaValue(@NonNull String key) { + return getMetaValue(key, CheckOrigin.LUCKPERMS_API).result(); + } + + public @NonNull StringResult> getPrefix(CheckOrigin origin) { + return this.prefix; + } + + @Override + public final @Nullable String getPrefix() { + return getPrefix(CheckOrigin.LUCKPERMS_API).result(); + } + + public @NonNull StringResult> getSuffix(CheckOrigin origin) { + return this.suffix; + } + + @Override + public final @Nullable String getSuffix() { + return getSuffix(CheckOrigin.LUCKPERMS_API).result(); + } + + protected Map>> getMetaResults(CheckOrigin origin) { + return this.meta; + } + + public final Map> getMeta(CheckOrigin origin) { + return Maps.transformValues(getMetaResults(origin), list -> Lists.transform(list, StringResult::result)); } @Override - public String getMetaValue(String key, MetaCheckEvent.Origin origin) { - String value = super.getMetaValue(key, origin); - this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), key, String.valueOf(value)); - return value; + public final @NonNull Map> getMeta() { + return getMeta(CheckOrigin.LUCKPERMS_API); } @Override - public String getPrefix(MetaCheckEvent.Origin origin) { - String value = super.getPrefix(origin); - this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Prefix.NODE_KEY, String.valueOf(value)); - return value; + public @NonNull SortedMap getPrefixes() { + return Maps.transformValues(this.prefixes, StringResult::result); + } + + @Override + public @NonNull SortedMap getSuffixes() { + return Maps.transformValues(this.suffixes, StringResult::result); + } + + public int getWeight(CheckOrigin origin) { + return this.weight; + } + + //@Override - not actually exposed in the API atm + public final int getWeight() { + return getWeight(CheckOrigin.LUCKPERMS_API); + } + + public @Nullable String getPrimaryGroup(CheckOrigin origin) { + return this.primaryGroup; } @Override - public String getSuffix(MetaCheckEvent.Origin origin) { - String value = super.getSuffix(origin); - this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Suffix.NODE_KEY, String.valueOf(value)); - return value; + public final @Nullable String getPrimaryGroup() { + return getPrimaryGroup(CheckOrigin.LUCKPERMS_API); } @Override - public Map> getMeta(MetaCheckEvent.Origin origin) { - return new MonitoredMetaMap(super.getMeta(origin), origin); + public @NonNull MetaStackDefinition getPrefixStackDefinition() { + return this.prefixDefinition; } @Override - public int getWeight(MetaCheckEvent.Origin origin) { - int value = super.getWeight(origin); - this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), "weight", String.valueOf(value)); - return value; + public @NonNull MetaStackDefinition getSuffixStackDefinition() { + return this.suffixDefinition; } @Override - public @Nullable String getPrimaryGroup(MetaCheckEvent.Origin origin) { - String value = super.getPrimaryGroup(origin); - this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), "primarygroup", String.valueOf(value)); - return value; + public @NonNull QueryOptions getQueryOptions() { + return this.queryOptions; } - private final class MonitoredMetaMap extends ForwardingMap> { - private final Map> delegate; - private final MetaCheckEvent.Origin origin; + private static final class LowerCaseMetaMap extends ForwardingMap>> { + private final Map>> delegate; - private MonitoredMetaMap(Map> delegate, MetaCheckEvent.Origin origin) { + private LowerCaseMetaMap(Map>> delegate) { this.delegate = delegate; - this.origin = origin; } @Override - protected Map> delegate() { + protected Map>> delegate() { return this.delegate; } @Override - public List get(Object k) { + public List> get(Object k) { if (k == null) { return null; } String key = (String) k; - List values = super.get(key); - MetaCache.this.plugin.getVerboseHandler().offerMetaCheckEvent(this.origin, MetaCache.this.metadata.getVerboseCheckInfo(), MetaCache.this.metadata.getQueryOptions(), key, String.valueOf(values)); - return values; + return super.get(key.toLowerCase(Locale.ROOT)); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaStackAccumulator.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaStackAccumulator.java index 89caedbd2..124dd7ca1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaStackAccumulator.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaStackAccumulator.java @@ -25,6 +25,8 @@ package me.lucko.luckperms.common.cacheddata.type; +import me.lucko.luckperms.common.cacheddata.result.StringResult; + import net.luckperms.api.metastacking.MetaStackDefinition; import net.luckperms.api.metastacking.MetaStackElement; import net.luckperms.api.node.ChatMetaType; @@ -36,6 +38,8 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; public class MetaStackAccumulator { private final MetaStackDefinition definition; @@ -57,6 +61,13 @@ public void offer(ChatMetaNode node) { } } + public List> getElements() { + return this.entries.stream() + .map(Entry::getNode) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + public String toFormattedString() { List elements = new LinkedList<>(); for (Entry entry : this.entries) { @@ -94,6 +105,35 @@ public String toFormattedString() { return sb.toString(); } + public StringResult> toResult() { + String formatted = toFormattedString(); + if (formatted == null) { + return StringResult.nullResult(); + } + + List> elements = getElements(); + + switch (elements.size()) { + case 0: + throw new AssertionError(); + case 1: + return StringResult.of(formatted, elements.get(0)); + default: { + Iterator> it = elements.iterator(); + StringResult> result = StringResult.of(formatted, it.next()); + + StringResult> root = result; + while (it.hasNext()) { + StringResult> nested = StringResult.of(it.next()); + root.setOverriddenResult(nested); + root = nested; + } + + return result; + } + } + } + private static final class Entry { private final MetaStackElement element; private final ChatMetaType type; diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java new file mode 100644 index 000000000..fd014eecd --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java @@ -0,0 +1,152 @@ +/* + * 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.cacheddata.type; + +import com.google.common.collect.ForwardingMap; + +import me.lucko.luckperms.common.cacheddata.CacheMetadata; +import me.lucko.luckperms.common.cacheddata.result.StringResult; +import me.lucko.luckperms.common.node.types.Prefix; +import me.lucko.luckperms.common.node.types.Suffix; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; + +import net.luckperms.api.cacheddata.CachedMetaData; +import net.luckperms.api.node.types.ChatMetaNode; +import net.luckperms.api.node.types.MetaNode; +import net.luckperms.api.query.QueryOptions; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Holds cached meta for a given context + */ +public class MonitoredMetaCache extends MetaCache implements CachedMetaData { + + /** The plugin instance */ + private final LuckPermsPlugin plugin; + + /** The metadata for this cache */ + private final CacheMetadata metadata; + + public MonitoredMetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, CacheMetadata metadata, MetaAccumulator sourceMeta) { + super(plugin, queryOptions, sourceMeta); + this.plugin = plugin; + this.metadata = metadata; + } + + @Override + @NonNull + public StringResult getMetaValue(String key, CheckOrigin origin) { + StringResult value = super.getMetaValue(key, origin); + this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), key, value); + return value; + } + + @Override + @NonNull + public StringResult> getPrefix(CheckOrigin origin) { + StringResult> value = super.getPrefix(origin); + this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Prefix.NODE_KEY, value); + return value; + } + + @Override + @NonNull + public StringResult> getSuffix(CheckOrigin origin) { + StringResult> value = super.getSuffix(origin); + this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Suffix.NODE_KEY, value); + return value; + } + + @Override + protected Map>> getMetaResults(CheckOrigin origin) { + return new MonitoredMetaMap(super.getMetaResults(origin), origin); + } + + @Override + public int getWeight(CheckOrigin origin) { + int value = super.getWeight(origin); + this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), "weight", StringResult.of(String.valueOf(value))); + return value; + } + + @Override + public @Nullable String getPrimaryGroup(CheckOrigin origin) { + String value = super.getPrimaryGroup(origin); + this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), "primarygroup", StringResult.of(value)); + return value; + } + + private final class MonitoredMetaMap extends ForwardingMap>> { + private final Map>> delegate; + private final CheckOrigin origin; + + private MonitoredMetaMap(Map>> delegate, CheckOrigin origin) { + this.delegate = delegate; + this.origin = origin; + } + + @Override + protected Map>> delegate() { + return this.delegate; + } + + @Override + public List> get(Object k) { + if (k == null) { + return null; + } + + String key = (String) k; + List> values = super.get(key); + + if (values == null || values.isEmpty()) { + MonitoredMetaCache.this.plugin.getVerboseHandler().offerMetaCheckEvent(this.origin, MonitoredMetaCache.this.metadata.getVerboseCheckInfo(), MonitoredMetaCache.this.metadata.getQueryOptions(), key, StringResult.nullResult()); + } else { + Iterator> it = values.iterator(); + StringResult result = it.next().copy(); + + StringResult root = result; + while (it.hasNext()) { + StringResult nested = it.next().copy(); + root.setOverriddenResult(nested); + root = nested; + } + + MonitoredMetaCache.this.plugin.getVerboseHandler().offerMetaCheckEvent(this.origin, MonitoredMetaCache.this.metadata.getVerboseCheckInfo(), MonitoredMetaCache.this.metadata.getQueryOptions(), key, result); + } + + return values; + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/PermissionCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/PermissionCache.java index 652a46c9a..c26d5a4c4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/PermissionCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/PermissionCache.java @@ -25,14 +25,17 @@ package me.lucko.luckperms.common.cacheddata.type; +import com.google.common.collect.Maps; + import me.lucko.luckperms.common.cacheddata.CacheMetadata; import me.lucko.luckperms.common.cacheddata.UsageTracked; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.calculator.CalculatorFactory; import me.lucko.luckperms.common.calculator.PermissionCalculator; -import me.lucko.luckperms.common.calculator.result.TristateResult; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.cacheddata.CachedPermissionData; +import net.luckperms.api.node.Node; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; @@ -55,12 +58,12 @@ public class PermissionCache extends UsageTracked implements CachedPermissionDat /** * The raw set of permission strings. */ - private final Map permissions; + private final Map permissions; /** - * An immutable copy of {@link #permissions} + * A string->boolean view of {@link #permissions} */ - private final Map permissionsUnmodifiable; + private final Map permissionsView; /** * The calculator instance responsible for resolving the raw permission strings in the permission map. @@ -69,10 +72,10 @@ public class PermissionCache extends UsageTracked implements CachedPermissionDat */ private final PermissionCalculator calculator; - public PermissionCache(QueryOptions queryOptions, CacheMetadata metadata, CalculatorFactory calculatorFactory, ConcurrentHashMap sourcePermissions) { + public PermissionCache(QueryOptions queryOptions, CacheMetadata metadata, CalculatorFactory calculatorFactory, ConcurrentHashMap sourcePermissions) { this.queryOptions = queryOptions; this.permissions = sourcePermissions; - this.permissionsUnmodifiable = Collections.unmodifiableMap(this.permissions); + this.permissionsView = Collections.unmodifiableMap(Maps.transformValues(this.permissions, Node::getValue)); this.calculator = calculatorFactory.build(queryOptions, metadata); this.calculator.setSourcePermissions(this.permissions); @@ -89,10 +92,10 @@ public PermissionCalculator getCalculator() { @Override public @NonNull Map getPermissionMap() { - return this.permissionsUnmodifiable; + return this.permissionsView; } - public TristateResult checkPermission(String permission, PermissionCheckEvent.Origin origin) { + public TristateResult checkPermission(String permission, CheckOrigin origin) { if (permission == null) { throw new NullPointerException("permission"); } @@ -101,7 +104,7 @@ public TristateResult checkPermission(String permission, PermissionCheckEvent.Or @Override public @NonNull Tristate checkPermission(@NonNull String permission) { - return checkPermission(permission, PermissionCheckEvent.Origin.LUCKPERMS_API).result(); + return checkPermission(permission, CheckOrigin.LUCKPERMS_API).result(); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaCache.java deleted file mode 100644 index 76e9c105a..000000000 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaCache.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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.cacheddata.type; - -import com.google.common.collect.ForwardingMap; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSortedMap; -import com.google.common.collect.Multimaps; - -import me.lucko.luckperms.common.cacheddata.UsageTracked; -import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; - -import net.luckperms.api.cacheddata.CachedMetaData; -import net.luckperms.api.metastacking.MetaStackDefinition; -import net.luckperms.api.query.QueryOptions; -import net.luckperms.api.query.meta.MetaValueSelector; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.SortedMap; - -/** - * Holds cached meta for a given context - */ -public class SimpleMetaCache extends UsageTracked implements CachedMetaData { - - private final LuckPermsPlugin plugin; - - /** The query options this container is holding data for */ - private final QueryOptions queryOptions; - - /* The data */ - protected final Map> meta; - protected final Map flattenedMeta; - protected final SortedMap prefixes; - protected final SortedMap suffixes; - protected final int weight; - protected final String primaryGroup; - private final MetaStackDefinition prefixDefinition; - private final MetaStackDefinition suffixDefinition; - private final String prefix; - private final String suffix; - - public SimpleMetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, MetaAccumulator sourceMeta) { - this.plugin = plugin; - this.queryOptions = queryOptions; - - Map> meta = Multimaps.asMap(ImmutableListMultimap.copyOf(sourceMeta.getMeta())); - - MetaValueSelector metaValueSelector = this.queryOptions.option(MetaValueSelector.KEY) - .orElseGet(() -> this.plugin.getConfiguration().get(ConfigKeys.META_VALUE_SELECTOR)); - - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (Map.Entry> e : meta.entrySet()) { - if (e.getValue().isEmpty()) { - continue; - } - - String selected = metaValueSelector.selectValue(e.getKey(), e.getValue()); - if (selected == null) { - throw new NullPointerException(metaValueSelector + " returned null"); - } - - builder.put(e.getKey(), selected); - } - this.flattenedMeta = builder.build(); - this.meta = new LowerCaseMetaMap(meta); - - this.prefixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getPrefixes()); - this.suffixes = ImmutableSortedMap.copyOfSorted(sourceMeta.getSuffixes()); - this.weight = sourceMeta.getWeight(); - this.primaryGroup = sourceMeta.getPrimaryGroup(); - this.prefixDefinition = sourceMeta.getPrefixDefinition(); - this.suffixDefinition = sourceMeta.getSuffixDefinition(); - this.prefix = sourceMeta.getPrefix(); - this.suffix = sourceMeta.getSuffix(); - } - - public String getMetaValue(String key, MetaCheckEvent.Origin origin) { - Objects.requireNonNull(key, "key"); - return this.flattenedMeta.get(key.toLowerCase(Locale.ROOT)); - } - - @Override - public final String getMetaValue(@NonNull String key) { - return getMetaValue(key, MetaCheckEvent.Origin.LUCKPERMS_API); - } - - public String getPrefix(MetaCheckEvent.Origin origin) { - return this.prefix; - } - - @Override - public final String getPrefix() { - return getPrefix(MetaCheckEvent.Origin.LUCKPERMS_API); - } - - public String getSuffix(MetaCheckEvent.Origin origin) { - return this.suffix; - } - - @Override - public final String getSuffix() { - return getSuffix(MetaCheckEvent.Origin.LUCKPERMS_API); - } - - public Map> getMeta(MetaCheckEvent.Origin origin) { - return this.meta; - } - - @Override - public final @NonNull Map> getMeta() { - return getMeta(MetaCheckEvent.Origin.LUCKPERMS_API); - } - - @Override - public @NonNull SortedMap getPrefixes() { - return this.prefixes; - } - - @Override - public @NonNull SortedMap getSuffixes() { - return this.suffixes; - } - - public int getWeight(MetaCheckEvent.Origin origin) { - return this.weight; - } - - //@Override - not actually exposed in the API atm - public final int getWeight() { - return getWeight(MetaCheckEvent.Origin.LUCKPERMS_API); - } - - public @Nullable String getPrimaryGroup(MetaCheckEvent.Origin origin) { - return this.primaryGroup; - } - - @Override - public final @Nullable String getPrimaryGroup() { - return getPrimaryGroup(MetaCheckEvent.Origin.LUCKPERMS_API); - } - - @Override - public @NonNull MetaStackDefinition getPrefixStackDefinition() { - return this.prefixDefinition; - } - - @Override - public @NonNull MetaStackDefinition getSuffixStackDefinition() { - return this.suffixDefinition; - } - - @Override - public @NonNull QueryOptions getQueryOptions() { - return this.queryOptions; - } - - private static final class LowerCaseMetaMap extends ForwardingMap> { - private final Map> delegate; - - private LowerCaseMetaMap(Map> delegate) { - this.delegate = delegate; - } - - @Override - protected Map> delegate() { - return this.delegate; - } - - @Override - public List get(Object k) { - if (k == null) { - return null; - } - - String key = (String) k; - return super.get(key.toLowerCase(Locale.ROOT)); - } - } - -} diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java index f7ed17485..c033dd41b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java @@ -25,6 +25,7 @@ package me.lucko.luckperms.common.cacheddata.type; +import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.query.meta.MetaValueSelector; import org.checkerframework.checker.nullness.qual.NonNull; @@ -43,7 +44,7 @@ public SimpleMetaValueSelector(Map strategies, Strategy defaul } @Override - public @NonNull String selectValue(@NonNull String key, @NonNull List values) { + public @NonNull MetaNode selectValue(@NonNull String key, @NonNull List values) { switch (values.size()) { case 0: throw new IllegalArgumentException("values is empty"); @@ -57,7 +58,7 @@ public SimpleMetaValueSelector(Map strategies, Strategy defaul public enum Strategy { INHERITANCE { @Override - public String select(List values) { + public MetaNode select(List values) { return values.get(0); } }, @@ -65,7 +66,7 @@ public String select(List values) { private final DoubleSelectionPredicate selection = (value, current) -> value > current; @Override - public String select(List values) { + public MetaNode select(List values) { return selectNumber(values, this.selection); } }, @@ -73,12 +74,12 @@ public String select(List values) { private final DoubleSelectionPredicate selection = (value, current) -> value < current; @Override - public String select(List values) { + public MetaNode select(List values) { return selectNumber(values, this.selection); } }; - public abstract String select(List values); + public abstract MetaNode select(List values); public static Strategy parse(String s) { try { @@ -94,15 +95,16 @@ private interface DoubleSelectionPredicate { boolean shouldSelect(double value, double current); } - private static String selectNumber(List values, DoubleSelectionPredicate selection) { + private static MetaNode selectNumber(List values, DoubleSelectionPredicate selection) { double current = 0; - String selected = null; + MetaNode selected = null; - for (String value : values) { + for (MetaNode node : values) { + String value = node.getMetaValue(); try { double parse = Double.parseDouble(value); if (selected == null || selection.shouldSelect(parse, current)) { - selected = value; + selected = node; current = parse; } } catch (NumberFormatException e) { diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/PermissionCalculator.java b/common/src/main/java/me/lucko/luckperms/common/calculator/PermissionCalculator.java index 1c1d73829..c3de5f829 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculator/PermissionCalculator.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/PermissionCalculator.java @@ -27,10 +27,12 @@ import me.lucko.luckperms.common.cache.LoadingMap; import me.lucko.luckperms.common.cacheddata.CacheMetadata; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; + +import net.luckperms.api.node.Node; import org.checkerframework.checker.nullness.qual.NonNull; @@ -71,7 +73,7 @@ public PermissionCalculator(LuckPermsPlugin plugin, CacheMetadata metadata, Coll * @param origin marks where this check originated from * @return the result */ - public TristateResult checkPermission(String permission, PermissionCheckEvent.Origin origin) { + public TristateResult checkPermission(String permission, CheckOrigin origin) { // get the result TristateResult result = this.lookupCache.get(permission); @@ -106,7 +108,7 @@ public TristateResult apply(@NonNull String permission) { * * @param sourceMap the source map */ - public synchronized void setSourcePermissions(Map sourceMap) { + public synchronized void setSourcePermissions(Map sourceMap) { for (PermissionProcessor processor : this.processors) { processor.setSource(sourceMap); processor.refresh(); diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractOverrideWildcardProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractOverrideWildcardProcessor.java new file mode 100644 index 000000000..e8546ea42 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractOverrideWildcardProcessor.java @@ -0,0 +1,58 @@ +/* + * 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.calculator.processor; + +import me.lucko.luckperms.common.cacheddata.result.TristateResult; + +import net.luckperms.api.util.Tristate; + +public abstract class AbstractOverrideWildcardProcessor extends AbstractPermissionProcessor implements PermissionProcessor { + private final boolean overrideWildcards; + + public AbstractOverrideWildcardProcessor(boolean overrideWildcards) { + this.overrideWildcards = overrideWildcards; + } + + private boolean canOverrideWildcard(TristateResult prev) { + return this.overrideWildcards && + (prev.processorClass() == WildcardProcessor.class || prev.processorClass() == SpongeWildcardProcessor.class) && + prev.result() == Tristate.TRUE; + } + + @Override + protected TristateResult hasPermissionOverride(TristateResult prev, String permission) { + if (canOverrideWildcard(prev)) { + TristateResult override = hasPermission(permission); + if (override.result() == Tristate.FALSE) { + override.setOverriddenResult(prev); + return override; + } + } + + return prev; + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractPermissionProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractPermissionProcessor.java index 198a7b69d..05fb3f705 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractPermissionProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractPermissionProcessor.java @@ -25,26 +25,28 @@ package me.lucko.luckperms.common.calculator.processor; -import me.lucko.luckperms.common.calculator.result.TristateResult; - -import java.util.Collections; -import java.util.Map; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +/** + * Abstract implementation of {@link PermissionProcessor} that splits behaviour for normal + * permission checks and override checks into two separate methods. + */ public abstract class AbstractPermissionProcessor implements PermissionProcessor { - protected Map sourceMap = Collections.emptyMap(); - - @Override - public void setSource(Map sourceMap) { - this.sourceMap = sourceMap; - } @Override - public TristateResult hasPermission(TristateResult prev, String permission) { + public final TristateResult hasPermission(TristateResult prev, String permission) { if (prev != TristateResult.UNDEFINED) { - return prev; + return hasPermissionOverride(prev, permission); } return hasPermission(permission); } - public abstract TristateResult hasPermission(String permission); + // Performs a regular permission check + protected abstract TristateResult hasPermission(String permission); + + // Performs an override permission check + protected TristateResult hasPermissionOverride(TristateResult prev, String permission) { + return prev; + } + } diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractSourceBasedProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractSourceBasedProcessor.java new file mode 100644 index 000000000..8b50500a3 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/AbstractSourceBasedProcessor.java @@ -0,0 +1,41 @@ +/* + * 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.calculator.processor; + +import net.luckperms.api.node.Node; + +import java.util.Collections; +import java.util.Map; + +public abstract class AbstractSourceBasedProcessor extends AbstractPermissionProcessor implements PermissionProcessor { + protected Map sourceMap = Collections.emptyMap(); + + @Override + public void setSource(Map sourceMap) { + this.sourceMap = sourceMap; + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/DirectProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/DirectProcessor.java index 2a19212d5..f2ab6828d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/DirectProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/DirectProcessor.java @@ -25,16 +25,14 @@ package me.lucko.luckperms.common.calculator.processor; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; -import net.luckperms.api.util.Tristate; - -public class DirectProcessor extends AbstractPermissionProcessor implements PermissionProcessor { +public class DirectProcessor extends AbstractSourceBasedProcessor implements PermissionProcessor { private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(DirectProcessor.class); @Override public TristateResult hasPermission(String permission) { - return RESULT_FACTORY.result(Tristate.of(this.sourceMap.get(permission))); + return RESULT_FACTORY.result(this.sourceMap.get(permission)); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/PermissionProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/PermissionProcessor.java index 0bca9d5e4..9920c74ce 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/PermissionProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/PermissionProcessor.java @@ -25,8 +25,10 @@ package me.lucko.luckperms.common.calculator.processor; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.calculator.PermissionCalculator; -import me.lucko.luckperms.common.calculator.result.TristateResult; + +import net.luckperms.api.node.Node; import java.util.Map; @@ -52,7 +54,7 @@ public interface PermissionProcessor { * * @param sourceMap the source map */ - default void setSource(Map sourceMap) { + default void setSource(Map sourceMap) { } diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/RegexProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/RegexProcessor.java index ae844f57c..d6a32505a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/RegexProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/RegexProcessor.java @@ -28,17 +28,17 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.node.types.RegexPermission; -import net.luckperms.api.util.Tristate; +import net.luckperms.api.node.Node; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Pattern; -public class RegexProcessor extends AbstractPermissionProcessor implements PermissionProcessor { +public class RegexProcessor extends AbstractSourceBasedProcessor implements PermissionProcessor { private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(RegexProcessor.class); private List> regexPermissions = Collections.emptyList(); @@ -56,7 +56,7 @@ public TristateResult hasPermission(String permission) { @Override public void refresh() { ImmutableList.Builder> builder = ImmutableList.builder(); - for (Map.Entry e : this.sourceMap.entrySet()) { + for (Map.Entry e : this.sourceMap.entrySet()) { RegexPermission.Builder regexPerm = RegexPermission.parse(e.getKey()); if (regexPerm == null) { continue; @@ -67,7 +67,7 @@ public void refresh() { continue; } - TristateResult value = RESULT_FACTORY.result(Tristate.of(e.getValue()), "pattern: " + pattern.pattern()); + TristateResult value = RESULT_FACTORY.result(e.getValue()); builder.add(Maps.immutableEntry(pattern, value)); } this.regexPermissions = builder.build(); diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/SpongeWildcardProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/SpongeWildcardProcessor.java index 382f29611..01c8fbb8d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/SpongeWildcardProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/SpongeWildcardProcessor.java @@ -25,12 +25,12 @@ package me.lucko.luckperms.common.calculator.processor; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.node.AbstractNode; -import net.luckperms.api.util.Tristate; +import net.luckperms.api.node.Node; -public class SpongeWildcardProcessor extends AbstractPermissionProcessor implements PermissionProcessor { +public class SpongeWildcardProcessor extends AbstractSourceBasedProcessor implements PermissionProcessor { private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(SpongeWildcardProcessor.class); @Override @@ -45,9 +45,9 @@ public TristateResult hasPermission(String permission) { node = node.substring(0, endIndex); if (!node.isEmpty()) { - Tristate t = Tristate.of(this.sourceMap.get(node)); - if (t != Tristate.UNDEFINED) { - return RESULT_FACTORY.result(t, "match: " + node); + Node n = this.sourceMap.get(node); + if (n != null) { + return RESULT_FACTORY.result(n); } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/WildcardProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/WildcardProcessor.java index fbb0faab9..d1d08b2f9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculator/processor/WildcardProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculator/processor/WildcardProcessor.java @@ -27,15 +27,16 @@ import com.google.common.collect.ImmutableMap; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.node.AbstractNode; +import net.luckperms.api.node.Node; import net.luckperms.api.util.Tristate; import java.util.Collections; import java.util.Map; -public class WildcardProcessor extends AbstractPermissionProcessor implements PermissionProcessor { +public class WildcardProcessor extends AbstractSourceBasedProcessor implements PermissionProcessor { private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(WildcardProcessor.class); public static final String WILDCARD_SUFFIX = ".*"; @@ -78,22 +79,22 @@ public TristateResult hasPermission(String permission) { @Override public void refresh() { ImmutableMap.Builder builder = ImmutableMap.builder(); - for (Map.Entry e : this.sourceMap.entrySet()) { + for (Map.Entry e : this.sourceMap.entrySet()) { String key = e.getKey(); if (!key.endsWith(WILDCARD_SUFFIX) || key.length() <= 2) { continue; } key = key.substring(0, key.length() - 2); - TristateResult value = RESULT_FACTORY.result(Tristate.of(e.getValue()), "match: " + key); + TristateResult value = RESULT_FACTORY.result(e.getValue()); builder.put(key, value); } this.wildcardPermissions = builder.build(); - Tristate state = Tristate.of(this.sourceMap.get(ROOT_WILDCARD)); - if (state == Tristate.UNDEFINED) { - state = Tristate.of(this.sourceMap.get(ROOT_WILDCARD_WITH_QUOTES)); + Node rootWildcard = this.sourceMap.get(ROOT_WILDCARD); + if (rootWildcard == null) { + rootWildcard = this.sourceMap.get(ROOT_WILDCARD_WITH_QUOTES); } - this.rootWildcardState = RESULT_FACTORY.result(state, "root"); + this.rootWildcardState = rootWildcard == null ? TristateResult.UNDEFINED : RESULT_FACTORY.result(rootWildcard); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/command/access/ArgumentPermissions.java b/common/src/main/java/me/lucko/luckperms/common/command/access/ArgumentPermissions.java index 1e04ce33d..b6b678f05 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/access/ArgumentPermissions.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/access/ArgumentPermissions.java @@ -25,9 +25,9 @@ package me.lucko.luckperms.common.command.access; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.cacheddata.type.PermissionCache; import me.lucko.luckperms.common.calculator.processor.DirectProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.HolderType; @@ -38,7 +38,7 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.context.Context; import net.luckperms.api.context.ContextSet; @@ -305,7 +305,7 @@ public static boolean checkGroup(LuckPermsPlugin plugin, Sender sender, String t } PermissionCache permissionData = user.getCachedData().getPermissionData(QueryOptionsImpl.DEFAULT_CONTEXTUAL.toBuilder().context(contextSet).build()); - TristateResult result = permissionData.checkPermission(Inheritance.key(targetGroupName), PermissionCheckEvent.Origin.INTERNAL); + TristateResult result = permissionData.checkPermission(Inheritance.key(targetGroupName), CheckOrigin.INTERNAL); return result.result() != Tristate.TRUE || result.processorClass() != DirectProcessor.class; } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheck.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheck.java index f56f69b0a..679f93d4d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheck.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheck.java @@ -25,8 +25,8 @@ package me.lucko.luckperms.common.commands.generic.permission; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.calculator.processor.WildcardProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; import me.lucko.luckperms.common.command.abstraction.CommandException; import me.lucko.luckperms.common.command.abstraction.GenericChildCommand; import me.lucko.luckperms.common.command.access.ArgumentPermissions; @@ -43,7 +43,7 @@ import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.kyori.adventure.text.Component; import net.luckperms.api.context.ImmutableContextSet; @@ -123,29 +123,13 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ // perform a "real" check QueryOptions queryOptions = target.getQueryOptions(); - TristateResult checkResult = target.getCachedData().getPermissionData(queryOptions).checkPermission(node, PermissionCheckEvent.Origin.INTERNAL); + TristateResult checkResult = target.getCachedData().getPermissionData(queryOptions).checkPermission(node, CheckOrigin.INTERNAL); Tristate result = checkResult.result(); - String processor; - String cause; + String processor = checkResult.processorClassFriendly(); + Node cause = checkResult.node(); ImmutableContextSet context = queryOptions.context(); - if (result != Tristate.UNDEFINED) { - Class processorClass = checkResult.processorClass(); - if (processorClass.getName().startsWith("me.lucko.luckperms.")) { - String simpleName = processorClass.getSimpleName(); - String platform = processorClass.getName().split("\\.")[3]; - processor = platform + "." + simpleName; - } else { - processor = processorClass.getName(); - } - - cause = checkResult.cause(); - } else { - processor = null; - cause = null; - } - // send results Message.PERMISSION_CHECK_RESULT.send(sender, node, result, processor, cause, context); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java index 21566678d..213fd587e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.common.commands.group; -import me.lucko.luckperms.common.cacheddata.type.MetaCache; +import me.lucko.luckperms.common.cacheddata.type.MonitoredMetaCache; import me.lucko.luckperms.common.command.abstraction.ChildCommand; import me.lucko.luckperms.common.command.access.ArgumentPermissions; import me.lucko.luckperms.common.command.access.CommandPermission; @@ -36,7 +36,7 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.node.Node; import net.luckperms.api.node.types.InheritanceNode; @@ -85,10 +85,10 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Group target, Argumen } QueryOptions queryOptions = plugin.getContextManager().getStaticQueryOptions(); - MetaCache data = target.getCachedData().getMetaData(queryOptions); - String prefix = data.getPrefix(MetaCheckEvent.Origin.INTERNAL); - String suffix = data.getSuffix(MetaCheckEvent.Origin.INTERNAL); - Map> meta = data.getMeta(MetaCheckEvent.Origin.INTERNAL); + MonitoredMetaCache data = target.getCachedData().getMetaData(queryOptions); + String prefix = data.getPrefix(CheckOrigin.INTERNAL).result(); + String suffix = data.getSuffix(CheckOrigin.INTERNAL).result(); + Map> meta = data.getMeta(CheckOrigin.INTERNAL); Message.GROUP_INFO_CONTEXTUAL_DATA.send(sender, prefix, suffix, meta); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java index 41a8cc7d2..5b20cf8ae 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.common.commands.user; -import me.lucko.luckperms.common.cacheddata.type.MetaCache; +import me.lucko.luckperms.common.cacheddata.type.MonitoredMetaCache; import me.lucko.luckperms.common.command.abstraction.ChildCommand; import me.lucko.luckperms.common.command.access.ArgumentPermissions; import me.lucko.luckperms.common.command.access.CommandPermission; @@ -37,7 +37,7 @@ import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.common.util.UniqueIdType; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.context.ContextSet; import net.luckperms.api.node.Node; @@ -100,11 +100,11 @@ public void execute(LuckPermsPlugin plugin, Sender sender, User target, Argument } ContextSet contextSet = queryOptions.context(); - MetaCache data = target.getCachedData().getMetaData(queryOptions); - String prefix = data.getPrefix(MetaCheckEvent.Origin.INTERNAL); - String suffix = data.getSuffix(MetaCheckEvent.Origin.INTERNAL); - String primaryGroup = data.getPrimaryGroup(MetaCheckEvent.Origin.INTERNAL); - Map> meta = data.getMeta(MetaCheckEvent.Origin.INTERNAL); + MonitoredMetaCache data = target.getCachedData().getMetaData(queryOptions); + String prefix = data.getPrefix(CheckOrigin.INTERNAL).result(); + String suffix = data.getSuffix(CheckOrigin.INTERNAL).result(); + String primaryGroup = data.getPrimaryGroup(CheckOrigin.INTERNAL); + Map> meta = data.getMeta(CheckOrigin.INTERNAL); Message.USER_INFO_CONTEXTUAL_DATA.send(sender, active, contextSet, prefix, suffix, primaryGroup, meta); } diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index 6976bd500..9ddf461c3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -338,6 +338,108 @@ static TextComponent prefixed(ComponentLike component) { .append(text(result, GRAY)) ); + Args1 VERBOSE_LOG_HOVER_TYPE = type -> translatable() + // "&aType: &2{}" + .key("luckperms.logs.verbose.hover.type-key") + .color(GREEN) + .append(text(": ")) + .append(text(type, DARK_GREEN)) + .build(); + + Args1 VERBOSE_LOG_HOVER_ORIGIN = origin -> translatable() + // "&bOrigin: &2{}" + .key("luckperms.logs.verbose.hover.origin-key") + .color(AQUA) + .append(text(": ")) + .append(text(origin, DARK_GREEN)) + .build(); + + Args1 VERBOSE_LOG_HOVER_CAUSE = causeNode -> translatable() + // "&bCause: {}" + .key("luckperms.logs.verbose.hover.cause-key") + .color(AQUA) + .append(text(": ")) + .apply(builder -> { + String origin = causeNode.getMetadata(InheritanceOriginMetadata.KEY) + .map(o -> o.getOrigin().getName()).orElse("?"); + + builder.append(translatable() + .key("luckperms.command.generic.permission.check.info.directly") + .color(GRAY) + .args( + text().color(AQUA).content(origin), + text(causeNode.getKey(), AQUA), + formatBoolean(causeNode.getValue()), + formatContextSet(causeNode.getContexts()) + ) + ); + }) + .build(); + + Args1 VERBOSE_LOG_HOVER_CAUSE_META = causeNode -> translatable() + // "&bCause: {}" + .key("luckperms.logs.verbose.hover.cause-key") + .color(AQUA) + .append(text(": ")) + .apply(builder -> { + String origin = causeNode.getMetadata(InheritanceOriginMetadata.KEY) + .map(o -> o.getOrigin().getName()).orElse("?"); + + builder.append(translatable() + .key("luckperms.command.generic.permission.check.info.directly") + .color(GRAY) + .args( + text().color(AQUA).content(origin), + text(causeNode.getMetaKey(), AQUA), + formatColoredValue(causeNode.getMetaValue()), + formatContextSet(causeNode.getContexts()) + ) + ); + }) + .build(); + + Args1 VERBOSE_LOG_HOVER_CONTEXT = set -> translatable() + // "&bContext: {}" + .key("luckperms.logs.verbose.hover.context-key") + .color(AQUA) + .append(text(": ")) + .append(formatContextSet(set)) + .build(); + + Args1 VERBOSE_LOG_HOVER_THREAD = threadName -> translatable() + // "&bThread: &f{}" + .key("luckperms.logs.verbose.hover.thread-key") + .color(AQUA) + .append(text(": ")) + .append(text(threadName, WHITE)) + .build(); + + Args0 VERBOSE_LOG_HOVER_TRACE_TITLE = () -> translatable() + // "&bTrace:" + .key("luckperms.logs.verbose.hover.trace-key") + .color(AQUA) + .append(text(':')) + .build(); + + Args1 VERBOSE_LOG_HOVER_TRACE_CONTENT = content -> text(content, GRAY); + + Args1 VERBOSE_LOG_HOVER_TRACE_OVERFLOW = overflow -> text() + // "&f... and {} more" + .color(WHITE) + .content("... ") + .append(translatable() + .key("luckperms.logs.verbose.hover.overflow") + .args(text(overflow)) + ) + .build(); + + Args1 VERBOSE_LOG_HOVER_PROCESSOR = processor -> translatable() + .key("luckperms.logs.verbose.hover.processor-key") + .color(AQUA) + .append(text(": ")) + .append(text(processor, DARK_GREEN)) + .build(); + Args1 EXPORT_LOG = msg -> prefixed(text() // "&3EXPORT &3&l> &f{}" .append(translatable("luckperms.logs.export-prefix", DARK_AQUA)) @@ -1832,7 +1934,7 @@ static TextComponent prefixed(ComponentLike component) { ) ); - Args5 PERMISSION_CHECK_RESULT = (permission, result, processor, cause, context) -> join(newline(), + Args5 PERMISSION_CHECK_RESULT = (permission, result, processor, causeNode, context) -> join(newline(), // &aPermission check for &b{}&a: // &3Result: {} // &3Processor: &f{} @@ -1867,10 +1969,22 @@ static TextComponent prefixed(ComponentLike component) { .append(translatable("luckperms.command.generic.permission.check.result.cause-key")) .append(text(": ")) .apply(builder -> { - if (cause == null) { + if (causeNode == null) { builder.append(translatable("luckperms.command.misc.none", AQUA)); } else { - builder.append(text(cause, WHITE)); + String origin = causeNode.getMetadata(InheritanceOriginMetadata.KEY) + .map(o -> o.getOrigin().getName()).orElse("?"); + + builder.append(translatable() + .key("luckperms.command.generic.permission.check.info.directly") + .color(GRAY) + .args( + text().color(AQUA).content(origin), + text(causeNode.getKey(), AQUA), + formatBoolean(causeNode.getValue()), + formatContextSet(causeNode.getContexts()) + ) + ); } })), prefixed(text() diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index 4583261f0..948d1b1e7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -354,19 +354,19 @@ public List resolveInheritanceTree(QueryOptions queryOptions) { return (List) inheritanceTree; } - public > M exportPermissions(IntFunction mapFactory, QueryOptions queryOptions, boolean convertToLowercase, boolean resolveShorthand) { + public > M exportPermissions(IntFunction mapFactory, QueryOptions queryOptions, boolean convertToLowercase, boolean resolveShorthand) { List entries = resolveInheritedNodes(queryOptions); M map = mapFactory.apply(entries.size()); processExportedPermissions(map, entries, convertToLowercase, resolveShorthand); return map; } - private static void processExportedPermissions(Map accumulator, List entries, boolean convertToLowercase, boolean resolveShorthand) { + private static void processExportedPermissions(Map accumulator, List entries, boolean convertToLowercase, boolean resolveShorthand) { for (Node node : entries) { if (convertToLowercase) { - accumulator.putIfAbsent(node.getKey().toLowerCase(Locale.ROOT), node.getValue()); + accumulator.putIfAbsent(node.getKey().toLowerCase(Locale.ROOT), node); } else { - accumulator.putIfAbsent(node.getKey(), node.getValue()); + accumulator.putIfAbsent(node.getKey(), node); } } @@ -375,9 +375,9 @@ private static void processExportedPermissions(Map accumulator, Collection shorthand = node.resolveShorthand(); for (String s : shorthand) { if (convertToLowercase) { - accumulator.putIfAbsent(s.toLowerCase(Locale.ROOT), node.getValue()); + accumulator.putIfAbsent(s.toLowerCase(Locale.ROOT), node); } else { - accumulator.putIfAbsent(s, node.getValue()); + accumulator.putIfAbsent(s, node); } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java index 36f7f8101..c8d58f720 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java @@ -40,6 +40,11 @@ public PermissionHolderIdentifier(HolderType type, String name) { this.name = name; } + public PermissionHolderIdentifier(String type, String name) { + this.type = type; + this.name = name; + } + @Override public @NonNull String getName() { return this.name; diff --git a/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java b/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java index 53a647f34..451fe11bb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java @@ -32,7 +32,7 @@ import me.lucko.luckperms.common.model.manager.group.GroupManager; import me.lucko.luckperms.common.node.types.Inheritance; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.model.data.DataType; import net.luckperms.api.node.Node; @@ -86,7 +86,7 @@ public boolean giveDefaultIfNeeded(User user) { // check that they are actually a member of their primary group, otherwise remove it if (this.plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION_METHOD).equals("stored")) { - String primaryGroup = user.getCachedData().getMetaData(this.plugin.getConfiguration().get(ConfigKeys.GLOBAL_QUERY_OPTIONS)).getPrimaryGroup(MetaCheckEvent.Origin.INTERNAL); + String primaryGroup = user.getCachedData().getMetaData(this.plugin.getConfiguration().get(ConfigKeys.GLOBAL_QUERY_OPTIONS)).getPrimaryGroup(CheckOrigin.INTERNAL); boolean memberOfPrimaryGroup = false; for (InheritanceNode node : globalGroups) { diff --git a/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java b/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java index 138bc55ea..cc532060a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/utils/NodeJsonSerializer.java @@ -34,6 +34,7 @@ import net.luckperms.api.node.Node; import net.luckperms.api.node.NodeBuilder; +import net.luckperms.api.node.metadata.types.InheritanceOriginMetadata; import java.time.Instant; import java.util.Collection; @@ -47,54 +48,81 @@ private NodeJsonSerializer() { } - public static JsonArray serializeNodes(Collection nodes) { - JsonArray arr = new JsonArray(); - for (Node node : nodes) { - JsonObject attributes = new JsonObject(); + public static JsonObject serializeNode(Node node, boolean includeInheritanceOrigin) { + JsonObject attributes = new JsonObject(); - attributes.addProperty("type", node.getType().name().toLowerCase(Locale.ROOT)); - attributes.addProperty("key", node.getKey()); - attributes.addProperty("value", node.getValue()); + attributes.addProperty("type", node.getType().name().toLowerCase(Locale.ROOT)); + attributes.addProperty("key", node.getKey()); + attributes.addProperty("value", node.getValue()); - Instant expiry = node.getExpiry(); - if (expiry != null) { - attributes.addProperty("expiry", expiry.getEpochSecond()); - } + Instant expiry = node.getExpiry(); + if (expiry != null) { + attributes.addProperty("expiry", expiry.getEpochSecond()); + } + + if (!node.getContexts().isEmpty()) { + attributes.add("context", ContextSetJsonSerializer.serialize(node.getContexts())); + } - if (!node.getContexts().isEmpty()) { - attributes.add("context", ContextSetJsonSerializer.serialize(node.getContexts())); + if (includeInheritanceOrigin) { + InheritanceOriginMetadata origin = node.getMetadata(InheritanceOriginMetadata.KEY).orElse(null); + if (origin != null) { + JsonObject metadata = new JsonObject(); + metadata.add("inheritanceOrigin", serializeInheritanceOrigin(origin)); + attributes.add("metadata", metadata); } + } - arr.add(attributes); + return attributes; + } + + private static JsonObject serializeInheritanceOrigin(InheritanceOriginMetadata origin) { + JsonObject obj = new JsonObject(); + obj.addProperty("type", origin.getOrigin().getType()); + obj.addProperty("name", origin.getOrigin().getName()); + return obj; + } + + public static JsonArray serializeNodes(Collection nodes) { + JsonArray arr = new JsonArray(); + for (Node node : nodes) { + arr.add(serializeNode(node, false)); } return arr; } - public static Set deserializeNodes(JsonArray arr) { - Set nodes = new HashSet<>(); - for (JsonElement ent : arr) { - JsonObject attributes = ent.getAsJsonObject(); + public static Node deserializeNode(JsonElement ent) { + JsonObject attributes = ent.getAsJsonObject(); - String key = attributes.get("key").getAsString(); + String key = attributes.get("key").getAsString(); - if (key.isEmpty()) { - continue; // skip - } + if (key.isEmpty()) { + return null; // skip + } - NodeBuilder builder = NodeBuilders.determineMostApplicable(key); + NodeBuilder builder = NodeBuilders.determineMostApplicable(key); - boolean value = attributes.get("value").getAsBoolean(); - builder.value(value); + boolean value = attributes.get("value").getAsBoolean(); + builder.value(value); - if (attributes.has("expiry")) { - builder.expiry(attributes.get("expiry").getAsLong()); - } + if (attributes.has("expiry")) { + builder.expiry(attributes.get("expiry").getAsLong()); + } - if (attributes.has("context")) { - builder.context(ContextSetJsonSerializer.deserialize(attributes.get("context"))); - } + if (attributes.has("context")) { + builder.context(ContextSetJsonSerializer.deserialize(attributes.get("context"))); + } + + return builder.build(); + } - nodes.add(builder.build()); + public static Set deserializeNodes(JsonArray arr) { + Set nodes = new HashSet<>(); + for (JsonElement ent : arr) { + Node node = deserializeNode(ent); + if (node != null) { + nodes.add(node); + } } return nodes; } diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java index ee2feaa69..732460494 100644 --- a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java @@ -36,7 +36,7 @@ import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.gson.GsonProvider; import me.lucko.luckperms.common.util.gson.JObject; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -161,7 +161,7 @@ public String uploadPasteData(BytebinClient bytebin, Sender sender, User user, P checks = new JObject(); for (Map.Entry node : this.view.getNodeEndings()) { String permission = prefix + node.getValue(); - checks.add(permission, checker.checkPermission(permission, PermissionCheckEvent.Origin.INTERNAL).result().name().toLowerCase(Locale.ROOT)); + checks.add(permission, checker.checkPermission(permission, CheckOrigin.INTERNAL).result().name().toLowerCase(Locale.ROOT)); } } else { checks = null; diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java index 98a09042f..4e489e6d3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java @@ -25,10 +25,12 @@ package me.lucko.luckperms.common.verbose; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.StringResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask; import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; import me.lucko.luckperms.common.verbose.event.VerboseEvent; @@ -74,7 +76,7 @@ public VerboseHandler(SchedulerAdapter scheduler) { * @param permission the permission which was checked for * @param result the result of the permission check */ - public void offerPermissionCheckEvent(PermissionCheckEvent.Origin origin, VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, String permission, TristateResult result) { + public void offerPermissionCheckEvent(CheckOrigin origin, VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, String permission, TristateResult result) { // don't bother even processing the check if there are no listeners registered if (!this.listening) { return; @@ -100,7 +102,7 @@ public void offerPermissionCheckEvent(PermissionCheckEvent.Origin origin, Verbos * @param key the meta key which was checked for * @param result the result of the meta check */ - public void offerMetaCheckEvent(MetaCheckEvent.Origin origin, VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, String key, String result) { + public void offerMetaCheckEvent(CheckOrigin origin, VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, String key, StringResult result) { // don't bother even processing the check if there are no listeners registered if (!this.listening) { return; diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java index 8d942b90b..ef7fd4acb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java @@ -27,7 +27,8 @@ import com.google.gson.JsonObject; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.Result; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.http.AbstractHttpClient; import me.lucko.luckperms.common.http.BytebinClient; import me.lucko.luckperms.common.http.UnsuccessfulRequestException; @@ -38,6 +39,7 @@ import me.lucko.luckperms.common.util.gson.GsonProvider; import me.lucko.luckperms.common.util.gson.JArray; import me.lucko.luckperms.common.util.gson.JObject; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; import me.lucko.luckperms.common.verbose.event.VerboseEvent; @@ -45,7 +47,8 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.format.NamedTextColor; +import net.luckperms.api.node.Node; +import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.query.QueryMode; import java.io.ByteArrayOutputStream; @@ -150,28 +153,6 @@ public void acceptEvent(VerboseEvent event) { } private void sendNotification(VerboseEvent event) { - if (this.notifiedSender.isConsole()) { - // just send as a raw message - if (event instanceof PermissionCheckEvent) { - PermissionCheckEvent permissionEvent = (PermissionCheckEvent) event; - Message.VERBOSE_LOG_PERMISSION.send(this.notifiedSender, - permissionEvent.getCheckTarget().describe(), - permissionEvent.getPermission(), - permissionEvent.getResult().result() - ); - } else if (event instanceof MetaCheckEvent) { - MetaCheckEvent metaEvent = (MetaCheckEvent) event; - Message.VERBOSE_LOG_META.send(this.notifiedSender, - metaEvent.getCheckTarget().describe(), - metaEvent.getKey(), - metaEvent.getResult() - ); - } else { - throw new IllegalArgumentException("Unknown event type: " + event); - } - return; - } - // form a text component from the check trace Component component; if (event instanceof PermissionCheckEvent) { @@ -186,69 +167,52 @@ private void sendNotification(VerboseEvent event) { component = Message.VERBOSE_LOG_META.build( metaEvent.getCheckTarget().describe(), metaEvent.getKey(), - metaEvent.getResult() + String.valueOf(metaEvent.getResult().result()) ); } else { throw new IllegalArgumentException("Unknown event type: " + event); } + // just send as a raw message + if (this.notifiedSender.isConsole()) { + this.notifiedSender.sendMessage(component); + return; + } + // build the hover text List hover = new ArrayList<>(); - if (event instanceof PermissionCheckEvent) { - PermissionCheckEvent permissionEvent = (PermissionCheckEvent) event; - hover.add(Component.text() - .append(Component.text("Type: ", NamedTextColor.GREEN)) - .append(Component.text("permission", NamedTextColor.DARK_GREEN)) - ); - hover.add(Component.text() - .append(Component.text("Origin: ", NamedTextColor.AQUA)) - .append(Component.text(permissionEvent.getOrigin().name(), NamedTextColor.DARK_GREEN)) - ); + hover.add(Message.VERBOSE_LOG_HOVER_TYPE.build(event.getType().toString())); + hover.add(Message.VERBOSE_LOG_HOVER_ORIGIN.build(event.getOrigin().name())); - TristateResult result = permissionEvent.getResult(); - if (result.processorClass() != null) { - hover.add(Component.text() - .append(Component.text("Processor: ", NamedTextColor.AQUA)) - .append(Component.text(result.processorClass().getName(), NamedTextColor.DARK_GREEN)) - ); - } - if (result.cause() != null) { - hover.add(Component.text() - .append(Component.text("Cause: ", NamedTextColor.AQUA)) - .append(Component.text(result.cause(), NamedTextColor.DARK_GREEN)) - ); + Result result = event.getResult(); + + if (result instanceof TristateResult) { + TristateResult tristateResult = (TristateResult) result; + + if (tristateResult.processorClass() != null) { + hover.add(Message.VERBOSE_LOG_HOVER_PROCESSOR.build(tristateResult.processorClassFriendly())); } } - if (event instanceof MetaCheckEvent) { - MetaCheckEvent metaEvent = (MetaCheckEvent) event; - hover.add(Component.text() - .append(Component.text("Type: ", NamedTextColor.GREEN)) - .append(Component.text("meta", NamedTextColor.DARK_GREEN)) - ); - hover.add(Component.text() - .append(Component.text("Origin: ", NamedTextColor.AQUA)) - .append(Component.text(metaEvent.getOrigin().name(), NamedTextColor.DARK_GREEN)) - ); + + Node node = result.node(); + if (node != null) { + if (node instanceof MetaNode) { + hover.add(Message.VERBOSE_LOG_HOVER_CAUSE_META.build((MetaNode) node)); + } else { + hover.add(Message.VERBOSE_LOG_HOVER_CAUSE.build(node)); + } } if (event.getCheckQueryOptions().mode() == QueryMode.CONTEXTUAL) { - hover.add(Component.text() - .append(Component.text("Context: ", NamedTextColor.AQUA)) - .append(Message.formatContextSet(event.getCheckQueryOptions().context())) - ); + hover.add(Message.VERBOSE_LOG_HOVER_CONTEXT.build(event.getCheckQueryOptions().context())); } - hover.add(Component.text() - .append(Component.text("Thread: ", NamedTextColor.AQUA)) - .append(Component.text(event.getCheckThread(), NamedTextColor.WHITE)) - ); + hover.add(Message.VERBOSE_LOG_HOVER_THREAD.build(event.getCheckThread())); - hover.add(Component.text() - .append(Component.text("Trace: ", NamedTextColor.AQUA)) - ); + hover.add(Message.VERBOSE_LOG_HOVER_TRACE_TITLE.build()); - Consumer printer = StackTracePrinter.elementToString(str -> hover.add(Component.text(str, NamedTextColor.GRAY))); + Consumer printer = StackTracePrinter.elementToString(str -> hover.add(Message.VERBOSE_LOG_HOVER_TRACE_CONTENT.build(str))); int overflow; if (shouldFilterStackTrace(event)) { overflow = CHAT_FILTERED_PRINTER.process(event.getCheckTrace(), printer); @@ -256,7 +220,7 @@ private void sendNotification(VerboseEvent event) { overflow = CHAT_UNFILTERED_PRINTER.process(event.getCheckTrace(), printer); } if (overflow != 0) { - hover.add(Component.text("... and " + overflow + " more", NamedTextColor.WHITE)); + hover.add(Message.VERBOSE_LOG_HOVER_TRACE_OVERFLOW.build(overflow)); } // send the message @@ -267,8 +231,8 @@ private void sendNotification(VerboseEvent event) { private static boolean shouldFilterStackTrace(VerboseEvent event) { if (event instanceof PermissionCheckEvent) { PermissionCheckEvent permissionEvent = (PermissionCheckEvent) event; - return permissionEvent.getOrigin() == PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK || - permissionEvent.getOrigin() == PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK; + return permissionEvent.getOrigin() == CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET || + permissionEvent.getOrigin() == CheckOrigin.PLATFORM_API_HAS_PERMISSION; } return false; } @@ -302,8 +266,8 @@ public String uploadPasteData(BytebinClient bytebin) throws IOException, Unsucce .add("truncated", truncated); JArray data = new JArray(); - for (VerboseEvent events : this.results) { - data.add(events.toJson(shouldFilterStackTrace(events) ? WEB_FILTERED_PRINTER : WEB_UNFILTERED_PRINTER)); + for (VerboseEvent event : this.results) { + data.add(event.toJson(shouldFilterStackTrace(event) ? WEB_FILTERED_PRINTER : WEB_UNFILTERED_PRINTER)); } this.results.clear(); diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/event/CheckOrigin.java b/common/src/main/java/me/lucko/luckperms/common/verbose/event/CheckOrigin.java new file mode 100644 index 000000000..6064f0b00 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/event/CheckOrigin.java @@ -0,0 +1,63 @@ +/* + * 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.verbose.event; + +/** + * Represents the origin of a meta check + */ +public enum CheckOrigin { + + /** + * Indicates the check was caused by a lookup in a platform API + */ + PLATFORM_API, + + /** + * Indicates the check was caused by a 'hasPermission' check on the platform + */ + PLATFORM_API_HAS_PERMISSION, + + /** + * Indicates the check was caused by a 'hasPermissionSet' type check on the platform + */ + PLATFORM_API_HAS_PERMISSION_SET, + + /** + * Indicates the check was caused by a 3rd party API call + */ + THIRD_PARTY_API, + + /** + * Indicates the check was caused by a LuckPerms API call + */ + LUCKPERMS_API, + + /** + * Indicates the check was caused by a LuckPerms internal + */ + INTERNAL + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/event/MetaCheckEvent.java b/common/src/main/java/me/lucko/luckperms/common/verbose/event/MetaCheckEvent.java index bcfa2114a..eba966d96 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/event/MetaCheckEvent.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/event/MetaCheckEvent.java @@ -25,6 +25,10 @@ package me.lucko.luckperms.common.verbose.event; +import com.google.gson.JsonArray; + +import me.lucko.luckperms.common.cacheddata.result.StringResult; +import me.lucko.luckperms.common.node.utils.NodeJsonSerializer; import me.lucko.luckperms.common.util.gson.JObject; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; @@ -34,11 +38,6 @@ public class MetaCheckEvent extends VerboseEvent { - /** - * The origin of the check - */ - private final Origin origin; - /** * The meta key which was checked for */ @@ -47,67 +46,67 @@ public class MetaCheckEvent extends VerboseEvent { /** * The result of the meta check */ - private final String result; + private final StringResult result; - public MetaCheckEvent(Origin origin, VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, long checkTime, Throwable checkTrace, String checkThread, String key, String result) { - super(checkTarget, checkQueryOptions, checkTime, checkTrace, checkThread); - this.origin = origin; + public MetaCheckEvent(CheckOrigin origin, VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, long checkTime, Throwable checkTrace, String checkThread, String key, StringResult result) { + super(origin, checkTarget, checkQueryOptions, checkTime, checkTrace, checkThread); this.key = key; this.result = result; } - public Origin getOrigin() { - return this.origin; - } - public String getKey() { return this.key; } - public String getResult() { + @Override + public StringResult getResult() { return this.result; } @Override - protected void serializeTo(JObject object) { - object.add("type", "meta") - .add("key", this.key) - .add("result", this.result) - .add("origin", this.origin.name().toLowerCase(Locale.ROOT)); + public VerboseEventType getType() { + return VerboseEventType.META; } @Override - public boolean eval(String variable) { - return variable.equals("meta") || - getCheckTarget().describe().equalsIgnoreCase(variable) || - getKey().toLowerCase(Locale.ROOT).startsWith(variable.toLowerCase(Locale.ROOT)) || - getResult().equalsIgnoreCase(variable); + protected void serializeTo(JObject object) { + object.add("key", this.key); + + object.add("result", String.valueOf(this.result.result())); + if (this.result != StringResult.nullResult()) { + object.add("resultInfo", serializeResult(this.result)); + } } - /** - * Represents the origin of a meta check - */ - public enum Origin { + private static JObject serializeResult(StringResult result) { + JObject object = new JObject(); + object.add("result", String.valueOf(result.result())); + + if (result.node() != null) { + object.add("node", NodeJsonSerializer.serializeNode(result.node(), true)); + } - /** - * Indicates the check was caused by a lookup in a platform API - */ - PLATFORM_API, + if (result.overriddenResult() != null) { + JsonArray overridden = new JsonArray(); - /** - * Indicates the check was caused by a 3rd party API call - */ - THIRD_PARTY_API, + StringResult next = result.overriddenResult(); + while (next != null) { + overridden.add(serializeResult(next).toJson()); + next = next.overriddenResult(); + } - /** - * Indicates the check was caused by a LuckPerms API call - */ - LUCKPERMS_API, + object.add("overridden", overridden); + } - /** - * Indicates the check was caused by a LuckPerms internal - */ - INTERNAL + return object; + } + @Override + public boolean eval(String variable) { + return variable.equals("meta") || + getCheckTarget().describe().equalsIgnoreCase(variable) || + getKey().toLowerCase(Locale.ROOT).startsWith(variable.toLowerCase(Locale.ROOT)) || + String.valueOf(getResult().result()).equalsIgnoreCase(variable); } + } diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/event/PermissionCheckEvent.java b/common/src/main/java/me/lucko/luckperms/common/verbose/event/PermissionCheckEvent.java index a646aa079..f047de036 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/event/PermissionCheckEvent.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/event/PermissionCheckEvent.java @@ -25,7 +25,10 @@ package me.lucko.luckperms.common.verbose.event; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import com.google.gson.JsonArray; + +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.node.utils.NodeJsonSerializer; import me.lucko.luckperms.common.util.gson.JObject; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; @@ -35,11 +38,6 @@ public class PermissionCheckEvent extends VerboseEvent { - /** - * The origin of the check - */ - private final Origin origin; - /** * The permission which was checked for */ @@ -50,43 +48,60 @@ public class PermissionCheckEvent extends VerboseEvent { */ private final TristateResult result; - public PermissionCheckEvent(Origin origin, VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, long checkTime, Throwable checkTrace, String checkThread, String permission, TristateResult result) { - super(checkTarget, checkQueryOptions, checkTime, checkTrace, checkThread); - this.origin = origin; + public PermissionCheckEvent(CheckOrigin origin, VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, long checkTime, Throwable checkTrace, String checkThread, String permission, TristateResult result) { + super(origin, checkTarget, checkQueryOptions, checkTime, checkTrace, checkThread); this.permission = permission; this.result = result; } - public Origin getOrigin() { - return this.origin; - } - public String getPermission() { return this.permission; } + @Override public TristateResult getResult() { return this.result; } + @Override + public VerboseEventType getType() { + return VerboseEventType.PERMISSION; + } + @Override protected void serializeTo(JObject object) { - object.add("type", "permission"); object.add("permission", this.permission); object.add("result", this.result.result().name().toLowerCase(Locale.ROOT)); - if (this.result.processorClass() != null || this.result.cause() != null) { - JObject resultInfo = new JObject(); - if (this.result.processorClass() != null) { - resultInfo.add("processorClass", this.result.processorClass().getName()); - } - if (this.result.cause() != null) { - resultInfo.add("cause", this.result.cause()); + if (this.result != TristateResult.UNDEFINED) { + object.add("resultInfo", serializeResult(this.result)); + } + } + + private static JObject serializeResult(TristateResult result) { + JObject object = new JObject(); + object.add("result", result.result().name().toLowerCase(Locale.ROOT)); + + if (result.processorClass() != null) { + object.add("processorClass", result.processorClass().getName()); + } + if (result.node() != null) { + object.add("node", NodeJsonSerializer.serializeNode(result.node(), true)); + } + + if (result.overriddenResult() != null) { + JsonArray overridden = new JsonArray(); + + TristateResult next = result.overriddenResult(); + while (next != null) { + overridden.add(serializeResult(next).toJson()); + next = next.overriddenResult(); } - object.add("resultInfo", resultInfo); + + object.add("overridden", overridden); } - object.add("origin", this.origin.name().toLowerCase(Locale.ROOT)); + return object; } @Override @@ -97,35 +112,4 @@ public boolean eval(String variable) { getResult().result().name().equalsIgnoreCase(variable); } - /** - * Represents the origin of a permission check - */ - public enum Origin { - - /** - * Indicates the check was caused by a 'hasPermission' check on the platform - */ - PLATFORM_PERMISSION_CHECK, - - /** - * Indicates the check was caused by a 'hasPermissionSet' type check on the platform - */ - PLATFORM_LOOKUP_CHECK, - - /** - * Indicates the check was caused by a 3rd party API call - */ - THIRD_PARTY_API, - - /** - * Indicates the check was caused by an LuckPerms API call - */ - LUCKPERMS_API, - - /** - * Indicates the check was caused by a LuckPerms internal - */ - INTERNAL - - } } diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java b/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java index 577d1fab0..14f500e06 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java @@ -27,6 +27,7 @@ import com.google.gson.JsonObject; +import me.lucko.luckperms.common.cacheddata.result.Result; import me.lucko.luckperms.common.util.StackTracePrinter; import me.lucko.luckperms.common.util.gson.JArray; import me.lucko.luckperms.common.util.gson.JObject; @@ -46,6 +47,11 @@ */ public abstract class VerboseEvent implements VariableEvaluator { + /** + * The origin of the check + */ + private final CheckOrigin origin; + /** * The name of the entity which was checked */ @@ -71,7 +77,8 @@ public abstract class VerboseEvent implements VariableEvaluator { */ private final String checkThread; - protected VerboseEvent(VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, long checkTime, Throwable checkTrace, String checkThread) { + protected VerboseEvent(CheckOrigin origin, VerboseCheckTarget checkTarget, QueryOptions checkQueryOptions, long checkTime, Throwable checkTrace, String checkThread) { + this.origin = origin; this.checkTarget = checkTarget; this.checkQueryOptions = checkQueryOptions; this.checkTime = checkTime; @@ -79,10 +86,16 @@ protected VerboseEvent(VerboseCheckTarget checkTarget, QueryOptions checkQueryOp this.checkThread = checkThread; } + public CheckOrigin getOrigin() { + return this.origin; + } + public VerboseCheckTarget getCheckTarget() { return this.checkTarget; } + public abstract Result getResult(); + public QueryOptions getCheckQueryOptions() { return this.checkQueryOptions; } @@ -99,10 +112,14 @@ public String getCheckThread() { return this.checkThread; } + public abstract VerboseEventType getType(); + protected abstract void serializeTo(JObject object); public JsonObject toJson(StackTracePrinter tracePrinter) { return new JObject() + .add("type", getType().toString()) + .add("origin", this.origin.name().toLowerCase(Locale.ROOT)) .add("who", new JObject() .add("identifier", this.checkTarget.describe()) .add("type", this.checkTarget.getType()) diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEventType.java b/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEventType.java new file mode 100644 index 000000000..367262962 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEventType.java @@ -0,0 +1,46 @@ +/* + * 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.verbose.event; + +import java.util.Locale; + +public enum VerboseEventType { + + /** + * {@link PermissionCheckEvent} + */ + PERMISSION, + + /** + * {@link MetaCheckEvent} + */ + META; + + @Override + public String toString() { + return name().toLowerCase(Locale.ROOT); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java index 24ddbb252..ab4a75a2f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java @@ -46,7 +46,7 @@ import me.lucko.luckperms.common.util.gson.GsonProvider; import me.lucko.luckperms.common.util.gson.JArray; import me.lucko.luckperms.common.util.gson.JObject; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.node.Node; @@ -233,7 +233,7 @@ public static void includeMatchingUsers(List holders, CollectioncomparingInt(u -> u.getCachedData().getMetaData(QueryOptions.nonContextual()).getWeight(MetaCheckEvent.Origin.INTERNAL)).reversed() + .comparingInt(u -> u.getCachedData().getMetaData(QueryOptions.nonContextual()).getWeight(CheckOrigin.INTERNAL)).reversed() // then, prioritise users we actually have a username for .thenComparing(u -> u.getUsername().isPresent(), ((Comparator) Boolean::compare).reversed()) // then sort according to their username diff --git a/common/src/main/resources/luckperms_en.properties b/common/src/main/resources/luckperms_en.properties index 69300a4db..220e48017 100644 --- a/common/src/main/resources/luckperms_en.properties +++ b/common/src/main/resources/luckperms_en.properties @@ -1,5 +1,13 @@ luckperms.logs.actionlog-prefix=LOG luckperms.logs.verbose-prefix=VB +luckperms.logs.verbose.hover.type-key=Type +luckperms.logs.verbose.hover.origin-key=Origin +luckperms.logs.verbose.hover.cause-key=Cause +luckperms.logs.verbose.hover.context-key=Context +luckperms.logs.verbose.hover.thread-key=Thread +luckperms.logs.verbose.hover.trace-key=Trace +luckperms.logs.verbose.hover.processor-key=Processor +luckperms.logs.verbose.hover.overflow=and {0} more luckperms.logs.export-prefix=EXPORT luckperms.commandsystem.available-commands=Use {0} to view available commands luckperms.commandsystem.command-not-recognised=Command not recognised diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCalculatorFactory.java b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCalculatorFactory.java index 3c78fa39e..00080670a 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCalculatorFactory.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCalculatorFactory.java @@ -69,7 +69,7 @@ public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metad boolean integratedOwner = queryOptions.option(FabricContextManager.INTEGRATED_SERVER_OWNER).orElse(false); if (integratedOwner && this.plugin.getConfiguration().get(ConfigKeys.FABRIC_INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS)) { - processors.add(new ServerOwnerProcessor()); + processors.add(ServerOwnerProcessor.INSTANCE); } return new PermissionCalculator(this.plugin, metadata, processors); diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/calculator/ServerOwnerProcessor.java b/fabric/src/main/java/me/lucko/luckperms/fabric/calculator/ServerOwnerProcessor.java index 61519020b..9426e7675 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/calculator/ServerOwnerProcessor.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/calculator/ServerOwnerProcessor.java @@ -25,8 +25,9 @@ package me.lucko.luckperms.fabric.calculator; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; import net.luckperms.api.util.Tristate; @@ -34,9 +35,15 @@ * Permission processor which is added to the owner of an Integrated server to * simply return true if no other processors match. */ -public class ServerOwnerProcessor extends AbstractPermissionProcessor { +public class ServerOwnerProcessor extends AbstractPermissionProcessor implements PermissionProcessor { private static final TristateResult TRUE_RESULT = new TristateResult.Factory(ServerOwnerProcessor.class).result(Tristate.TRUE); + public static final ServerOwnerProcessor INSTANCE = new ServerOwnerProcessor(); + + private ServerOwnerProcessor() { + + } + @Override public TristateResult hasPermission(String permission) { return TRUE_RESULT; diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/PermissionCheckListener.java b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/PermissionCheckListener.java index 515bd5d7b..cfef3c0f6 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/PermissionCheckListener.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/PermissionCheckListener.java @@ -26,10 +26,10 @@ package me.lucko.luckperms.fabric.listeners; import me.lucko.fabric.api.permissions.v0.PermissionCheckEvent; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent.Origin; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import me.lucko.luckperms.fabric.LPFabricPlugin; import me.lucko.luckperms.fabric.model.MixinUser; @@ -83,7 +83,7 @@ private TriState onOtherPermissionCheck(CommandSource source, String permission) String name = ((ServerCommandSource) source).getName(); VerboseCheckTarget target = VerboseCheckTarget.internal(name); - this.plugin.getVerboseHandler().offerPermissionCheckEvent(Origin.PLATFORM_PERMISSION_CHECK, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.UNDEFINED); + this.plugin.getVerboseHandler().offerPermissionCheckEvent(CheckOrigin.PLATFORM_API_HAS_PERMISSION, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.UNDEFINED); this.plugin.getPermissionRegistry().offer(permission); } diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java index 3232599ee..2ee381204 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java @@ -29,7 +29,7 @@ import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.locale.TranslationManager; import me.lucko.luckperms.common.model.User; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import me.lucko.luckperms.fabric.context.FabricContextManager; import me.lucko.luckperms.fabric.event.PlayerChangeWorldCallback; import me.lucko.luckperms.fabric.model.MixinUser; @@ -133,7 +133,7 @@ public Tristate hasPermission(String permission, QueryOptions queryOptions) { } PermissionCache data = user.getCachedData().getPermissionData(queryOptions); - return data.checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK).result(); + return data.checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result(); } diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/ChildProcessor.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/ChildProcessor.java index 75a2f63d0..53c479ded 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/ChildProcessor.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/ChildProcessor.java @@ -25,9 +25,9 @@ package me.lucko.luckperms.nukkit.calculator; -import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractSourceBasedProcessor; import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; import me.lucko.luckperms.nukkit.LPNukkitPlugin; import net.luckperms.api.util.Tristate; @@ -40,7 +40,7 @@ /** * Permission Processor for Nukkits "child" permission system. */ -public class ChildProcessor extends AbstractPermissionProcessor implements PermissionProcessor { +public class ChildProcessor extends AbstractSourceBasedProcessor implements PermissionProcessor { private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(ChildProcessor.class); private final LPNukkitPlugin plugin; @@ -62,10 +62,10 @@ public TristateResult hasPermission(String permission) { @Override public void refresh() { Map childPermissions = new HashMap<>(); - this.sourceMap.forEach((key, value) -> { - Map children = this.plugin.getPermissionMap().getChildPermissions(key, value); + this.sourceMap.forEach((key, node) -> { + Map children = this.plugin.getPermissionMap().getChildPermissions(key, node.getValue()); children.forEach((childKey, childValue) -> { - childPermissions.put(childKey, RESULT_FACTORY.result(Tristate.of(childValue), "parent: " + key)); + childPermissions.put(childKey, RESULT_FACTORY.resultWithOverride(node, Tristate.of(childValue))); }); }); this.childPermissions = childPermissions; diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/DefaultPermissionMapProcessor.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/DefaultPermissionMapProcessor.java new file mode 100644 index 000000000..2e7a9c398 --- /dev/null +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/DefaultPermissionMapProcessor.java @@ -0,0 +1,58 @@ +/* + * 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.calculator; + +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor; +import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; +import me.lucko.luckperms.nukkit.LPNukkitPlugin; + +import net.luckperms.api.util.Tristate; + +/** + * Permission Processor for Nukkits "default" permission system. + */ +public class DefaultPermissionMapProcessor extends AbstractPermissionProcessor implements PermissionProcessor { + private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(DefaultPermissionMapProcessor.class); + + private final LPNukkitPlugin plugin; + private final boolean isOp; + + public DefaultPermissionMapProcessor(LPNukkitPlugin plugin, boolean isOp) { + this.plugin = plugin; + this.isOp = isOp; + } + + @Override + public TristateResult hasPermission(String permission) { + Tristate t = this.plugin.getDefaultPermissionMap().lookupDefaultPermission(permission, this.isOp); + if (t != Tristate.UNDEFINED) { + return RESULT_FACTORY.result(t); + } + + return TristateResult.UNDEFINED; + } +} diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/DefaultsProcessor.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/DefaultsProcessor.java deleted file mode 100644 index b619f3c1b..000000000 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/DefaultsProcessor.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.calculator; - -import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import me.lucko.luckperms.common.calculator.processor.SpongeWildcardProcessor; -import me.lucko.luckperms.common.calculator.processor.WildcardProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; -import me.lucko.luckperms.nukkit.LPNukkitPlugin; -import me.lucko.luckperms.nukkit.inject.PermissionDefault; - -import net.luckperms.api.util.Tristate; - -/** - * Permission Processor for Nukkits "default" permission system. - */ -public class DefaultsProcessor implements PermissionProcessor { - private static final TristateResult.Factory DEFAULT_PERMISSION_MAP_RESULT_FACTORY = new TristateResult.Factory(DefaultsProcessor.class, "default permission map"); - private static final TristateResult.Factory PERMISSION_MAP_RESULT_FACTORY = new TristateResult.Factory(DefaultsProcessor.class, "permission map"); - - private final LPNukkitPlugin plugin; - private final boolean overrideWildcards; - private final boolean isOp; - - public DefaultsProcessor(LPNukkitPlugin plugin, boolean overrideWildcards, boolean isOp) { - this.plugin = plugin; - this.overrideWildcards = overrideWildcards; - this.isOp = isOp; - } - - private boolean canOverrideWildcard(TristateResult prev) { - return this.overrideWildcards && - (prev.processorClass() == WildcardProcessor.class || prev.processorClass() == SpongeWildcardProcessor.class) && - prev.result() == Tristate.TRUE; - } - - @Override - public TristateResult hasPermission(TristateResult prev, String permission) { - if (prev != TristateResult.UNDEFINED) { - // Check to see if the result should be overridden - if (canOverrideWildcard(prev)) { - PermissionDefault def = PermissionDefault.fromPermission(this.plugin.getPermissionMap().get(permission)); - if (def != null) { - if (def == PermissionDefault.FALSE || this.isOp && def == PermissionDefault.NOT_OP) { - return PERMISSION_MAP_RESULT_FACTORY.result(Tristate.FALSE, "permission map (overriding wildcard): " + prev.cause()); - } - } - } - - return prev; - } - - Tristate t = this.plugin.getDefaultPermissionMap().lookupDefaultPermission(permission, this.isOp); - if (t != Tristate.UNDEFINED) { - return DEFAULT_PERMISSION_MAP_RESULT_FACTORY.result(t); - } - - PermissionDefault def = PermissionDefault.fromPermission(this.plugin.getPermissionMap().get(permission)); - if (def == null) { - return TristateResult.UNDEFINED; - } - return PERMISSION_MAP_RESULT_FACTORY.result(Tristate.of(def.getValue(this.isOp))); - } -} diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/NukkitCalculatorFactory.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/NukkitCalculatorFactory.java index 564282c3c..e2a5aad76 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/NukkitCalculatorFactory.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/NukkitCalculatorFactory.java @@ -52,7 +52,7 @@ public NukkitCalculatorFactory(LPNukkitPlugin plugin) { @Override public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metadata) { - List processors = new ArrayList<>(7); + List processors = new ArrayList<>(8); processors.add(new DirectProcessor()); @@ -75,7 +75,8 @@ public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metad boolean op = queryOptions.option(NukkitContextManager.OP_OPTION).orElse(false); if (metadata.getHolderType() == HolderType.USER && this.plugin.getConfiguration().get(ConfigKeys.APPLY_NUKKIT_DEFAULT_PERMISSIONS)) { boolean overrideWildcards = this.plugin.getConfiguration().get(ConfigKeys.APPLY_DEFAULT_NEGATIONS_BEFORE_WILDCARDS); - processors.add(new DefaultsProcessor(this.plugin, overrideWildcards, op)); + processors.add(new DefaultPermissionMapProcessor(this.plugin, op)); + processors.add(new PermissionMapProcessor(this.plugin, overrideWildcards, op)); } if (op) { diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/OpProcessor.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/OpProcessor.java index de22d9575..c2c9b61e3 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/OpProcessor.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/OpProcessor.java @@ -25,8 +25,9 @@ package me.lucko.luckperms.nukkit.calculator; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor; import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; import net.luckperms.api.util.Tristate; @@ -34,7 +35,7 @@ * Permission Processor which is added for opped users, to simply return true if * no other processors match. */ -public final class OpProcessor implements PermissionProcessor { +public final class OpProcessor extends AbstractPermissionProcessor implements PermissionProcessor { private static final TristateResult TRUE_RESULT = new TristateResult.Factory(OpProcessor.class).result(Tristate.TRUE); public static final OpProcessor INSTANCE = new OpProcessor(); @@ -44,10 +45,7 @@ private OpProcessor() { } @Override - public TristateResult hasPermission(TristateResult prev, String permission) { - if (prev != TristateResult.UNDEFINED) { - return prev; - } + public TristateResult hasPermission(String permission) { return TRUE_RESULT; } } diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/PermissionMapProcessor.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/PermissionMapProcessor.java new file mode 100644 index 000000000..d287c647d --- /dev/null +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/calculator/PermissionMapProcessor.java @@ -0,0 +1,59 @@ +/* + * 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.calculator; + +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractOverrideWildcardProcessor; +import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; +import me.lucko.luckperms.nukkit.LPNukkitPlugin; +import me.lucko.luckperms.nukkit.inject.PermissionDefault; + +import net.luckperms.api.util.Tristate; + +/** + * Permission Processor for Nukkits "default" permission system. + */ +public class PermissionMapProcessor extends AbstractOverrideWildcardProcessor implements PermissionProcessor { + private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(PermissionMapProcessor.class); + + private final LPNukkitPlugin plugin; + private final boolean isOp; + + public PermissionMapProcessor(LPNukkitPlugin plugin, boolean overrideWildcards, boolean isOp) { + super(overrideWildcards); + this.plugin = plugin; + this.isOp = isOp; + } + + @Override + public TristateResult hasPermission(String permission) { + PermissionDefault def = PermissionDefault.fromPermission(this.plugin.getPermissionMap().get(permission)); + if (def == null) { + return TristateResult.UNDEFINED; + } + return RESULT_FACTORY.result(Tristate.of(def.getValue(this.isOp))); + } +} diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissible.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissible.java index b7296a4b6..81db6d90d 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissible.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/LuckPermsPermissible.java @@ -28,14 +28,14 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.model.User; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import me.lucko.luckperms.nukkit.LPNukkitPlugin; -import me.lucko.luckperms.nukkit.calculator.DefaultsProcessor; import me.lucko.luckperms.nukkit.calculator.OpProcessor; +import me.lucko.luckperms.nukkit.calculator.PermissionMapProcessor; import me.lucko.luckperms.nukkit.inject.PermissionDefault; import net.luckperms.api.query.QueryOptions; @@ -144,13 +144,13 @@ public boolean isPermissionSet(String permission) { } QueryOptions queryOptions = this.queryOptionsSupplier.getQueryOptions(); - TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK); + TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET); if (result.result() == Tristate.UNDEFINED) { return false; } // ignore matches made from looking up in the permission map (replicate nukkit behaviour) - if (result.processorClass() == DefaultsProcessor.class && "permission map".equals(result.cause())) { + if (result.processorClass() == PermissionMapProcessor.class) { return false; } @@ -174,7 +174,7 @@ public boolean hasPermission(String permission) { } QueryOptions queryOptions = this.queryOptionsSupplier.getQueryOptions(); - return this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK).result().asBoolean(); + return this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result().asBoolean(); } @Override @@ -184,7 +184,7 @@ public boolean hasPermission(Permission permission) { } QueryOptions queryOptions = this.queryOptionsSupplier.getQueryOptions(); - TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission.getName(), PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK); + TristateResult result = this.user.getCachedData().getPermissionData(queryOptions).checkPermission(permission.getName(), CheckOrigin.PLATFORM_API_HAS_PERMISSION); // override default op handling using the Permission class we have if (result.processorClass() == OpProcessor.class && this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) { diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/MonitoredPermissibleBase.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/MonitoredPermissibleBase.java index d728d4014..f74bdd9e4 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/MonitoredPermissibleBase.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/inject/permissible/MonitoredPermissibleBase.java @@ -25,12 +25,12 @@ package me.lucko.luckperms.nukkit.inject.permissible; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; import me.lucko.luckperms.common.verbose.VerboseHandler; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.util.Tristate; @@ -68,8 +68,8 @@ public MonitoredPermissibleBase(LuckPermsPlugin plugin, PermissibleBase delegate this.initialised = true; } - private void logCheck(PermissionCheckEvent.Origin origin, String permission, boolean result) { - this.plugin.getVerboseHandler().offerPermissionCheckEvent(origin, this.verboseCheckTarget, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.of(Tristate.of(result))); + private void logCheck(CheckOrigin origin, String permission, boolean result) { + this.plugin.getVerboseHandler().offerPermissionCheckEvent(origin, this.verboseCheckTarget, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.forMonitoredResult(Tristate.of(result))); this.plugin.getPermissionRegistry().offer(permission); } @@ -84,7 +84,7 @@ public boolean isPermissionSet(String permission) { } final boolean result = this.delegate.isPermissionSet(permission); - logCheck(PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK, permission, result); + logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET, permission, result); return result; } @@ -95,7 +95,7 @@ public boolean isPermissionSet(Permission permission) { } final boolean result = this.delegate.isPermissionSet(permission); - logCheck(PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK, permission.getName(), result); + logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET, permission.getName(), result); return result; } @@ -106,7 +106,7 @@ public boolean hasPermission(String permission) { } final boolean result = this.delegate.hasPermission(permission); - logCheck(PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK, permission, result); + logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION, permission, result); return result; } @@ -117,7 +117,7 @@ public boolean hasPermission(Permission permission) { } final boolean result = this.delegate.hasPermission(permission); - logCheck(PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK, permission.getName(), result); + logCheck(CheckOrigin.PLATFORM_API_HAS_PERMISSION, permission.getName(), result); return result; } diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectCollectionProxy.java b/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectCollectionProxy.java index 660be87ea..fe66e8df7 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectCollectionProxy.java +++ b/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectCollectionProxy.java @@ -80,7 +80,7 @@ public SubjectCollectionProxy(LPSubjectCollection handle) { @Override public @NonNull CompletableFuture> loadSubjects(@NonNull Set set) { - return this.handle.loadSubjects(set).thenApply(subs -> subs.stream().collect(ImmutableCollectors.toMap(LPSubject::getIdentifier, LPSubject::sponge))); + return this.handle.loadSubjects(set).thenApply(subs -> subs.stream().collect(ImmutableCollectors.toMap(lpSubject -> lpSubject.getIdentifier().getName(), LPSubject::sponge))); } @Override diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java index 153fb7531..41df403a6 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java @@ -28,6 +28,7 @@ import com.google.common.collect.ImmutableList; import net.luckperms.api.context.ImmutableContextSet; +import net.luckperms.api.model.PermissionHolder; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; @@ -45,7 +46,7 @@ public interface LPSubject { LPPermissionService getService(); - String getIdentifier(); + PermissionHolder.Identifier getIdentifier(); default LPSubjectReference toReference() { return getService().getReferenceFactory().obtain(this); diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java index 87566a65c..9efa6d95b 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java @@ -79,7 +79,7 @@ public LPSubjectReference deserialize(String serializedReference) { public LPSubjectReference obtain(LPSubject subject) { Objects.requireNonNull(subject, "subject"); - LPSubjectReference reference = obtain(subject.getParentCollection().getIdentifier(), subject.getIdentifier()); + LPSubjectReference reference = obtain(subject.getParentCollection().getIdentifier(), subject.getIdentifier().getName()); ((CachedSubjectReference) reference).fillCache(subject); return reference; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/DefaultsProcessor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/DefaultsProcessor.java deleted file mode 100644 index 0a5bd8f9a..000000000 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/DefaultsProcessor.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.sponge.calculator; - -import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import me.lucko.luckperms.common.calculator.processor.SpongeWildcardProcessor; -import me.lucko.luckperms.common.calculator.processor.WildcardProcessor; -import me.lucko.luckperms.common.calculator.result.TristateResult; -import me.lucko.luckperms.sponge.service.model.LPPermissionService; -import me.lucko.luckperms.sponge.service.model.LPSubject; - -import net.luckperms.api.query.QueryOptions; -import net.luckperms.api.util.Tristate; - -public abstract class DefaultsProcessor implements PermissionProcessor { - private static final TristateResult.Factory TYPE_DEFAULTS_RESULT_FACTORY = new TristateResult.Factory(DefaultsProcessor.class, "type defaults"); - private static final TristateResult.Factory ROOT_DEFAULTS_RESULT_FACTORY = new TristateResult.Factory(DefaultsProcessor.class, "root defaults"); - - protected final LPPermissionService service; - private final QueryOptions queryOptions; - private final boolean overrideWildcards; - - public DefaultsProcessor(LPPermissionService service, QueryOptions queryOptions, boolean overrideWildcards) { - this.service = service; - this.queryOptions = queryOptions; - this.overrideWildcards = overrideWildcards; - } - - protected abstract LPSubject getTypeDefaults(); - - private boolean canOverrideWildcard(TristateResult prev) { - return this.overrideWildcards && - (prev.processorClass() == WildcardProcessor.class || prev.processorClass() == SpongeWildcardProcessor.class) && - prev.result() == Tristate.TRUE; - } - - @Override - public TristateResult hasPermission(TristateResult prev, String permission) { - if (prev != TristateResult.UNDEFINED) { - // Check to see if the result should be overridden - if (canOverrideWildcard(prev)) { - Tristate t = getTypeDefaults().getPermissionValue(this.queryOptions, permission); - if (t == Tristate.FALSE) { - return TYPE_DEFAULTS_RESULT_FACTORY.result(Tristate.FALSE, "type defaults (overriding wildcard): " + prev.cause()); - } - - t = this.service.getRootDefaults().getPermissionValue(this.queryOptions, permission); - if (t == Tristate.FALSE) { - return ROOT_DEFAULTS_RESULT_FACTORY.result(Tristate.FALSE, "root defaults (overriding wildcard): " + prev.cause()); - } - } - - return prev; - } - - Tristate t = getTypeDefaults().getPermissionValue(this.queryOptions, permission); - if (t != Tristate.UNDEFINED) { - return TYPE_DEFAULTS_RESULT_FACTORY.result(t); - } - - t = this.service.getRootDefaults().getPermissionValue(this.queryOptions, permission); - if (t != Tristate.UNDEFINED) { - return ROOT_DEFAULTS_RESULT_FACTORY.result(t); - } - - return TristateResult.UNDEFINED; - } -} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/FixedDefaultsProcessor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/FixedTypeDefaultsProcessor.java similarity index 88% rename from sponge/src/main/java/me/lucko/luckperms/sponge/calculator/FixedDefaultsProcessor.java rename to sponge/src/main/java/me/lucko/luckperms/sponge/calculator/FixedTypeDefaultsProcessor.java index 7fb466d22..7846e6d82 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/FixedDefaultsProcessor.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/FixedTypeDefaultsProcessor.java @@ -30,10 +30,10 @@ import net.luckperms.api.query.QueryOptions; -public class FixedDefaultsProcessor extends DefaultsProcessor { +public class FixedTypeDefaultsProcessor extends TypeDefaultsProcessor { private final LPSubject defaultsSubject; - public FixedDefaultsProcessor(LPPermissionService service, QueryOptions queryOptions, LPSubject defaultsSubject, boolean overrideWildcards) { + public FixedTypeDefaultsProcessor(LPPermissionService service, QueryOptions queryOptions, LPSubject defaultsSubject, boolean overrideWildcards) { super(service, queryOptions, overrideWildcards); this.defaultsSubject = defaultsSubject; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/GroupDefaultsProcessor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/GroupTypeDefaultsProcessor.java similarity index 88% rename from sponge/src/main/java/me/lucko/luckperms/sponge/calculator/GroupDefaultsProcessor.java rename to sponge/src/main/java/me/lucko/luckperms/sponge/calculator/GroupTypeDefaultsProcessor.java index 8a4fb8050..830162893 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/GroupDefaultsProcessor.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/GroupTypeDefaultsProcessor.java @@ -31,8 +31,8 @@ import net.luckperms.api.query.QueryOptions; -public class GroupDefaultsProcessor extends DefaultsProcessor implements PermissionProcessor { - public GroupDefaultsProcessor(LPPermissionService service, QueryOptions queryOptions, boolean overrideWildcards) { +public class GroupTypeDefaultsProcessor extends TypeDefaultsProcessor implements PermissionProcessor { + public GroupTypeDefaultsProcessor(LPPermissionService service, QueryOptions queryOptions, boolean overrideWildcards) { super(service, queryOptions, overrideWildcards); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/RootDefaultsProcessor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/RootDefaultsProcessor.java new file mode 100644 index 000000000..0d8aac0e6 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/RootDefaultsProcessor.java @@ -0,0 +1,57 @@ +/* + * 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.sponge.calculator; + +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractOverrideWildcardProcessor; +import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; +import me.lucko.luckperms.sponge.service.model.LPPermissionService; + +import net.luckperms.api.query.QueryOptions; +import net.luckperms.api.util.Tristate; + +public class RootDefaultsProcessor extends AbstractOverrideWildcardProcessor implements PermissionProcessor { + private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(RootDefaultsProcessor.class); + + protected final LPPermissionService service; + private final QueryOptions queryOptions; + + public RootDefaultsProcessor(LPPermissionService service, QueryOptions queryOptions, boolean overrideWildcards) { + super(overrideWildcards); + this.service = service; + this.queryOptions = queryOptions; + } + + @Override + public TristateResult hasPermission(String permission) { + Tristate t = this.service.getRootDefaults().getPermissionValue(this.queryOptions, permission); + if (t != Tristate.UNDEFINED) { + return RESULT_FACTORY.result(t); + } + + return TristateResult.UNDEFINED; + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/SpongeCalculatorFactory.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/SpongeCalculatorFactory.java index 89ade487a..f55d15745 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/SpongeCalculatorFactory.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/SpongeCalculatorFactory.java @@ -70,10 +70,11 @@ public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metad if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_DEFAULT_SUBJECTS)) { boolean overrideWildcards = this.plugin.getConfiguration().get(ConfigKeys.APPLY_DEFAULT_NEGATIONS_BEFORE_WILDCARDS); if (metadata.getHolderType() == HolderType.USER) { - processors.add(new UserDefaultsProcessor(this.plugin.getService(), queryOptions, overrideWildcards)); + processors.add(new UserTypeDefaultsProcessor(this.plugin.getService(), queryOptions, overrideWildcards)); } else if (metadata.getHolderType() == HolderType.GROUP) { - processors.add(new GroupDefaultsProcessor(this.plugin.getService(), queryOptions, overrideWildcards)); + processors.add(new GroupTypeDefaultsProcessor(this.plugin.getService(), queryOptions, overrideWildcards)); } + processors.add(new RootDefaultsProcessor(this.plugin.getService(), queryOptions, overrideWildcards)); } return new PermissionCalculator(this.plugin, metadata, processors); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/TypeDefaultsProcessor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/TypeDefaultsProcessor.java new file mode 100644 index 000000000..99af378ca --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/TypeDefaultsProcessor.java @@ -0,0 +1,60 @@ +/* + * 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.sponge.calculator; + +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractOverrideWildcardProcessor; +import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; +import me.lucko.luckperms.sponge.service.model.LPPermissionService; +import me.lucko.luckperms.sponge.service.model.LPSubject; + +import net.luckperms.api.query.QueryOptions; +import net.luckperms.api.util.Tristate; + +public abstract class TypeDefaultsProcessor extends AbstractOverrideWildcardProcessor implements PermissionProcessor { + private static final TristateResult.Factory RESULT_FACTORY = new TristateResult.Factory(TypeDefaultsProcessor.class); + + protected final LPPermissionService service; + private final QueryOptions queryOptions; + + public TypeDefaultsProcessor(LPPermissionService service, QueryOptions queryOptions, boolean overrideWildcards) { + super(overrideWildcards); + this.service = service; + this.queryOptions = queryOptions; + } + + protected abstract LPSubject getTypeDefaults(); + + @Override + public TristateResult hasPermission(String permission) { + Tristate t = getTypeDefaults().getPermissionValue(this.queryOptions, permission); + if (t != Tristate.UNDEFINED) { + return RESULT_FACTORY.result(t); + } + + return TristateResult.UNDEFINED; + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/UserDefaultsProcessor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/UserTypeDefaultsProcessor.java similarity index 88% rename from sponge/src/main/java/me/lucko/luckperms/sponge/calculator/UserDefaultsProcessor.java rename to sponge/src/main/java/me/lucko/luckperms/sponge/calculator/UserTypeDefaultsProcessor.java index 30e02cd4e..de5d7da3c 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/UserDefaultsProcessor.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculator/UserTypeDefaultsProcessor.java @@ -31,8 +31,8 @@ import net.luckperms.api.query.QueryOptions; -public class UserDefaultsProcessor extends DefaultsProcessor implements PermissionProcessor { - public UserDefaultsProcessor(LPPermissionService service, QueryOptions queryOptions, boolean overrideWildcards) { +public class UserTypeDefaultsProcessor extends TypeDefaultsProcessor implements PermissionProcessor { + public UserTypeDefaultsProcessor(LPPermissionService service, QueryOptions queryOptions, boolean overrideWildcards) { super(service, queryOptions, overrideWildcards); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java index f42a5e092..fd21a9e92 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java @@ -66,7 +66,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subject if (subjectData.addParent(contextSet, subject.toReference()).join()) { SpongeCommandUtils.sendPrefixed(sender, "&aAdded parent &b" + subject.getParentCollection().getIdentifier() + - "&a/&b" + subject.getIdentifier() + "&a in context " + SpongeCommandUtils.contextToString(contextSet)); + "&a/&b" + subject.getIdentifier().getName() + "&a in context " + SpongeCommandUtils.contextToString(contextSet)); } else { SpongeCommandUtils.sendPrefixed(sender, "Unable to add parent. Does the Subject already have it added?"); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java index a3d3ece54..f1126f9d1 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java @@ -66,7 +66,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subject if (subjectData.removeParent(contextSet, subject.toReference()).join()) { SpongeCommandUtils.sendPrefixed(sender, "&aRemoved parent &b" + subject.getParentCollection().getIdentifier() + - "&a/&b" + subject.getIdentifier() + "&a in context " + SpongeCommandUtils.contextToString(contextSet)); + "&a/&b" + subject.getIdentifier().getName() + "&a in context " + SpongeCommandUtils.contextToString(contextSet)); } else { SpongeCommandUtils.sendPrefixed(sender, "Unable to remove parent. Are you sure the Subject has it added?"); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeParentCommand.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeParentCommand.java index 3eaf93df1..12df23de3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeParentCommand.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeParentCommand.java @@ -111,7 +111,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Void v, ArgumentList if (args.size() < 2) { List subjects = collection.getLoadedSubjects().stream() - .map(LPSubject::getIdentifier) + .map(lpSubject -> lpSubject.getIdentifier().getName()) .collect(Collectors.toList()); if (subjects.size() > 50) { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubject.java index e8870b80d..3f8864096 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubject.java @@ -28,16 +28,18 @@ import com.google.common.collect.ImmutableList; import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator; +import me.lucko.luckperms.common.cacheddata.type.MetaCache; import me.lucko.luckperms.common.graph.TraversalAlgorithm; import me.lucko.luckperms.common.query.QueryOptionsImpl; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.service.inheritance.SubjectInheritanceGraph; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectReference; import net.luckperms.api.context.ImmutableContextSet; +import net.luckperms.api.node.Node; +import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; @@ -67,9 +69,9 @@ public LPSubject getDefaults() { @Override public abstract CalculatedSubjectData getTransientSubjectData(); - public Map getCombinedPermissions(QueryOptions filter) { - Map permissions; - Map merging; + public Map getCombinedPermissions(QueryOptions filter) { + Map permissions; + Map merging; switch (getParentCollection().getResolutionOrder()) { case TRANSIENT_FIRST: permissions = getTransientSubjectData().resolvePermissions(filter); @@ -83,18 +85,18 @@ public Map getCombinedPermissions(QueryOptions filter) { throw new AssertionError(); } - for (Map.Entry entry : merging.entrySet()) { + for (Map.Entry entry : merging.entrySet()) { permissions.putIfAbsent(entry.getKey(), entry.getValue()); } return permissions; } - public void resolveAllPermissions(Map accumulator, QueryOptions filter) { + public void resolveAllPermissions(Map accumulator, QueryOptions filter) { SubjectInheritanceGraph graph = new SubjectInheritanceGraph(filter); Iterable traversal = graph.traverse(TraversalAlgorithm.DEPTH_FIRST_PRE_ORDER, this); for (CalculatedSubject subject : traversal) { - for (Map.Entry entry : subject.getCombinedPermissions(filter).entrySet()) { + for (Map.Entry entry : subject.getCombinedPermissions(filter).entrySet()) { accumulator.putIfAbsent(entry.getKey(), entry.getValue()); } } @@ -132,9 +134,9 @@ public Set resolveAllParents(QueryOptions filter) { return result; } - public Map getCombinedOptions(QueryOptions filter) { - Map options; - Map merging; + public Map getCombinedOptions(QueryOptions filter) { + Map options; + Map merging; switch (getParentCollection().getResolutionOrder()) { case TRANSIENT_FIRST: options = getTransientSubjectData().resolveOptions(filter); @@ -148,7 +150,7 @@ public Map getCombinedOptions(QueryOptions filter) { throw new AssertionError(); } - for (Map.Entry entry : merging.entrySet()) { + for (Map.Entry entry : merging.entrySet()) { options.putIfAbsent(entry.getKey(), entry.getValue()); } return options; @@ -160,8 +162,8 @@ public Map resolveAllOptions(QueryOptions filter) { Iterable traversal = graph.traverse(TraversalAlgorithm.DEPTH_FIRST_PRE_ORDER, this); for (CalculatedSubject subject : traversal) { - for (Map.Entry entry : subject.getCombinedOptions(filter).entrySet()) { - result.putIfAbsent(entry.getKey(), entry.getValue()); + for (MetaNode entry : subject.getCombinedOptions(filter).values()) { + result.putIfAbsent(entry.getMetaKey(), entry.getMetaValue()); } } @@ -170,18 +172,20 @@ public Map resolveAllOptions(QueryOptions filter) { public void resolveAllOptions(MetaAccumulator accumulator, QueryOptions filter) { SubjectInheritanceGraph graph = new SubjectInheritanceGraph(filter); + Iterable traversal = graph.traverse(TraversalAlgorithm.DEPTH_FIRST_PRE_ORDER, this); for (CalculatedSubject subject : traversal) { - for (Map.Entry entry : subject.getCombinedOptions(filter).entrySet()) { - accumulator.accumulateMeta(entry.getKey(), entry.getValue()); + for (MetaNode entry : subject.getCombinedOptions(filter).values()) { + accumulator.accumulateNode(entry); } } + accumulator.complete(); } @Override public Tristate getPermissionValue(QueryOptions options, String permission) { - return this.cachedData.getPermissionData(options).checkPermission(permission, PermissionCheckEvent.Origin.INTERNAL).result(); + return this.cachedData.getPermissionData(options).checkPermission(permission, CheckOrigin.INTERNAL).result(); } @Override @@ -201,7 +205,7 @@ public ImmutableList getParents(ImmutableContextSet contexts @Override public Optional getOption(ImmutableContextSet contexts, String key) { - return Optional.ofNullable(this.cachedData.getMetaData(QueryOptionsImpl.DEFAULT_CONTEXTUAL.toBuilder().context(contexts).build()).getMetaValue(key, MetaCheckEvent.Origin.PLATFORM_API)); + return Optional.ofNullable(((MetaCache) this.cachedData.getMetaData(QueryOptionsImpl.DEFAULT_CONTEXTUAL.toBuilder().context(contexts).build())).getMetaValue(key, CheckOrigin.PLATFORM_API).result()); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectCachedDataManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectCachedDataManager.java index eb03ca20a..9172dd839 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectCachedDataManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectCachedDataManager.java @@ -40,11 +40,13 @@ import me.lucko.luckperms.common.metastacking.StandardStackElements; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; -import me.lucko.luckperms.sponge.calculator.FixedDefaultsProcessor; +import me.lucko.luckperms.sponge.calculator.FixedTypeDefaultsProcessor; +import me.lucko.luckperms.sponge.calculator.RootDefaultsProcessor; import net.luckperms.api.metastacking.DuplicateRemovalFunction; import net.luckperms.api.metastacking.MetaStackDefinition; import net.luckperms.api.node.ChatMetaType; +import net.luckperms.api.node.Node; import net.luckperms.api.query.QueryOptions; import java.util.ArrayList; @@ -68,7 +70,7 @@ public class CalculatedSubjectCachedDataManager extends AbstractCachedDataManage @Override protected CacheMetadata getMetadataForQueryOptions(QueryOptions queryOptions) { - VerboseCheckTarget target = VerboseCheckTarget.of(this.subject.getParentCollection().getIdentifier(), this.subject.getIdentifier()); + VerboseCheckTarget target = VerboseCheckTarget.of(this.subject.getParentCollection().getIdentifier(), this.subject.getIdentifier().getName()); return new CacheMetadata(null, target, queryOptions); } @@ -88,7 +90,7 @@ protected MetaStackDefinition getDefaultMetaStackDefinition(ChatMetaType type) { } @Override - protected > M resolvePermissions(IntFunction mapFactory, QueryOptions queryOptions) { + protected > M resolvePermissions(IntFunction mapFactory, QueryOptions queryOptions) { M map = mapFactory.apply(16); this.subject.resolveAllPermissions(map, queryOptions); return map; @@ -101,13 +103,14 @@ protected void resolveMeta(MetaAccumulator accumulator, QueryOptions queryOption @Override public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metadata) { - List processors = new ArrayList<>(4); + List processors = new ArrayList<>(5); processors.add(new DirectProcessor()); processors.add(new SpongeWildcardProcessor()); processors.add(new WildcardProcessor()); if (!this.subject.getParentCollection().isDefaultsCollection()) { - processors.add(new FixedDefaultsProcessor(this.subject.getService(), queryOptions, this.subject.getDefaults(), true)); + processors.add(new FixedTypeDefaultsProcessor(this.subject.getService(), queryOptions, this.subject.getDefaults(), true)); + processors.add(new RootDefaultsProcessor(this.subject.getService(), queryOptions, true)); } return new PermissionCalculator(getPlugin(), metadata, processors); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java index 74e6b881c..9637e2160 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java @@ -31,6 +31,9 @@ import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.context.comparator.ContextSetComparator; +import me.lucko.luckperms.common.model.InheritanceOrigin; +import me.lucko.luckperms.common.node.types.Meta; +import me.lucko.luckperms.common.node.types.Permission; import me.lucko.luckperms.sponge.service.ProxyFactory; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubject; @@ -40,6 +43,10 @@ import net.luckperms.api.context.ContextSatisfyMode; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.model.data.DataType; +import net.luckperms.api.node.Node; +import net.luckperms.api.node.metadata.types.InheritanceOriginMetadata; +import net.luckperms.api.node.types.MetaNode; +import net.luckperms.api.node.types.PermissionNode; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; @@ -134,21 +141,32 @@ public ImmutableMap getPermissions(ImmutableContextSet contexts return ImmutableMap.copyOf(this.permissions.getOrDefault(contexts, ImmutableMap.of())); } - public Map resolvePermissions(QueryOptions filter) { + public Map resolvePermissions(QueryOptions filter) { // get relevant entries - SortedMap> sorted = new TreeMap<>(ContextSetComparator.reverse()); + SortedMap> sorted = new TreeMap<>(ContextSetComparator.reverse()); for (Map.Entry> entry : this.permissions.entrySet()) { if (!filter.satisfies(entry.getKey(), defaultSatisfyMode())) { continue; } - sorted.put(entry.getKey(), entry.getValue()); + Map nodeMap = new HashMap<>(); + entry.getValue().forEach((key, value) -> { + PermissionNode node = Permission.builder() + .permission(key) + .value(value) + .context(entry.getKey()) + .withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.parentSubject.getIdentifier())) + .build(); + nodeMap.put(key, node); + }); + + sorted.put(entry.getKey(), nodeMap); } // flatten - Map result = new HashMap<>(); - for (Map map : sorted.values()) { - for (Map.Entry e : map.entrySet()) { + Map result = new HashMap<>(); + for (Map map : sorted.values()) { + for (Map.Entry e : map.entrySet()) { result.putIfAbsent(e.getKey(), e.getValue()); } } @@ -288,21 +306,32 @@ public ImmutableMap getOptions(ImmutableContextSet contexts) { return ImmutableMap.copyOf(this.options.getOrDefault(contexts, ImmutableMap.of())); } - public Map resolveOptions(QueryOptions filter) { + public Map resolveOptions(QueryOptions filter) { // get relevant entries - SortedMap> sorted = new TreeMap<>(ContextSetComparator.reverse()); + SortedMap> sorted = new TreeMap<>(ContextSetComparator.reverse()); for (Map.Entry> entry : this.options.entrySet()) { if (!filter.satisfies(entry.getKey(), defaultSatisfyMode())) { continue; } - sorted.put(entry.getKey(), entry.getValue()); + Map nodeMap = new HashMap<>(); + entry.getValue().forEach((key, value) -> { + MetaNode node = Meta.builder() + .key(key) + .value(value) + .context(entry.getKey()) + .withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.parentSubject.getIdentifier())) + .build(); + nodeMap.put(key, node); + }); + + sorted.put(entry.getKey(), nodeMap); } // flatten - Map result = new HashMap<>(); - for (Map map : sorted.values()) { - for (Map.Entry e : map.entrySet()) { + Map result = new HashMap<>(); + for (Map map : sorted.values()) { + for (Map.Entry e : map.entrySet()) { result.putIfAbsent(e.getKey(), e.getValue()); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java index e70d68dbc..b3fa83b37 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java @@ -30,6 +30,8 @@ import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; +import net.luckperms.api.model.PermissionHolder; + import org.spongepowered.api.command.CommandSource; import java.util.Optional; @@ -43,8 +45,8 @@ public GroupSubject(LPSpongePlugin plugin, SpongeGroup parent) { } @Override - public String getIdentifier() { - return this.parent.getObjectName(); + public PermissionHolder.Identifier getIdentifier() { + return this.parent.getIdentifier(); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubject.java index 341147e9f..40d3dd50c 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubject.java @@ -35,8 +35,7 @@ import me.lucko.luckperms.common.node.types.Inheritance; import me.lucko.luckperms.common.node.types.Prefix; import me.lucko.luckperms.common.node.types.Suffix; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.model.SpongeGroup; import me.lucko.luckperms.sponge.service.LuckPermsService; @@ -113,7 +112,7 @@ public PermissionHolderSubjectData getTransientSubjectData() { @Override public Tristate getPermissionValue(QueryOptions options, String permission) { - return this.parent.getCachedData().getPermissionData(options).checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK).result(); + return this.parent.getCachedData().getPermissionData(options).checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET).result(); } @Override @@ -148,20 +147,20 @@ public ImmutableList getParents(ImmutableContextSet contexts public Optional getOption(ImmutableContextSet contexts, String s) { MetaCache data = this.parent.getCachedData().getMetaData(this.plugin.getContextManager().formQueryOptions(contexts)); if (s.equalsIgnoreCase(Prefix.NODE_KEY)) { - String prefix = data.getPrefix(MetaCheckEvent.Origin.PLATFORM_API); + String prefix = data.getPrefix(CheckOrigin.PLATFORM_API).result(); if (prefix != null) { return Optional.of(prefix); } } if (s.equalsIgnoreCase(Suffix.NODE_KEY)) { - String suffix = data.getSuffix(MetaCheckEvent.Origin.PLATFORM_API); + String suffix = data.getSuffix(CheckOrigin.PLATFORM_API).result(); if (suffix != null) { return Optional.of(suffix); } } - String val = data.getMetaValue(s, MetaCheckEvent.Origin.PLATFORM_API); + String val = data.getMetaValue(s, CheckOrigin.PLATFORM_API).result(); if (val != null) { return Optional.of(val); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java index 8648e0e12..bc6fa3fb4 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java @@ -30,6 +30,8 @@ import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; +import net.luckperms.api.model.PermissionHolder; + import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandSource; @@ -46,8 +48,8 @@ public UserSubject(LPSpongePlugin plugin, SpongeUser parent) { } @Override - public String getIdentifier() { - return this.parent.getUniqueId().toString(); + public PermissionHolder.Identifier getIdentifier() { + return this.parent.getIdentifier(); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java index 2c6a05c0e..6c6675a71 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java @@ -26,6 +26,7 @@ package me.lucko.luckperms.sponge.service.model.persisted; import me.lucko.luckperms.common.cache.BufferedRequest; +import me.lucko.luckperms.common.model.PermissionHolderIdentifier; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.ProxyFactory; @@ -37,6 +38,7 @@ import me.lucko.luckperms.sponge.service.model.calculated.CalculatedSubjectData; import me.lucko.luckperms.sponge.service.model.calculated.MonitoredSubjectData; +import net.luckperms.api.model.PermissionHolder; import net.luckperms.api.model.data.DataType; import org.spongepowered.api.command.CommandSource; @@ -54,7 +56,7 @@ public class PersistedSubject extends CalculatedSubject implements LPSubject { /** * The subjects identifier */ - private final String identifier; + private final PermissionHolderIdentifier identifier; /** * The parent collection @@ -81,7 +83,7 @@ public PersistedSubject(LuckPermsService service, PersistedCollection parentColl super(service.getPlugin()); this.service = service; this.parentCollection = parentCollection; - this.identifier = identifier; + this.identifier = new PermissionHolderIdentifier(parentCollection.getIdentifier(), identifier); this.subjectData = new PersistedSubjectData(this, DataType.NORMAL, service) { @Override @@ -156,7 +158,7 @@ public ProxiedSubject sponge() { } @Override - public String getIdentifier() { + public PermissionHolder.Identifier getIdentifier() { return this.identifier; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectStorage.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectStorage.java index 6eb811c37..717c9cd05 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectStorage.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectStorage.java @@ -110,7 +110,7 @@ private Path resolveFile(String collectionIdentifier, String subjectIdentifier) * @throws IOException if the write fails */ public void saveToFile(PersistedSubject subject) throws IOException { - Path subjectFile = resolveFile(subject.getParentCollection().getIdentifier(), subject.getIdentifier()); + Path subjectFile = resolveFile(subject.getParentCollection().getIdentifier(), subject.getIdentifier().getName()); saveToFile(SubjectDataContainer.copyOf(subject.getSubjectData()), subjectFile); } diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/MonitoringPermissionCheckListener.java b/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/MonitoringPermissionCheckListener.java index 81db28ec4..730f5b875 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/MonitoringPermissionCheckListener.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/MonitoringPermissionCheckListener.java @@ -33,10 +33,10 @@ import com.velocitypowered.api.permission.PermissionSubject; import com.velocitypowered.api.proxy.Player; -import me.lucko.luckperms.common.calculator.result.TristateResult; +import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import me.lucko.luckperms.velocity.LPVelocityPlugin; import me.lucko.luckperms.velocity.service.CompatibilityUtil; @@ -91,7 +91,7 @@ private final class MonitoredPermissionFunction implements PermissionFunction { // report result Tristate result = CompatibilityUtil.convertTristate(setting); - MonitoringPermissionCheckListener.this.plugin.getVerboseHandler().offerPermissionCheckEvent(PermissionCheckEvent.Origin.PLATFORM_LOOKUP_CHECK, this.verboseCheckTarget, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.of(result)); + MonitoringPermissionCheckListener.this.plugin.getVerboseHandler().offerPermissionCheckEvent(CheckOrigin.PLATFORM_API_HAS_PERMISSION_SET, this.verboseCheckTarget, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.forMonitoredResult(result)); MonitoringPermissionCheckListener.this.plugin.getPermissionRegistry().offer(permission); return setting; diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/service/PlayerPermissionProvider.java b/velocity/src/main/java/me/lucko/luckperms/velocity/service/PlayerPermissionProvider.java index 5287f04db..b1756f389 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/service/PlayerPermissionProvider.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/service/PlayerPermissionProvider.java @@ -34,7 +34,7 @@ import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.common.model.User; -import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; import org.checkerframework.checker.nullness.qual.NonNull; @@ -57,6 +57,6 @@ public PlayerPermissionProvider(Player player, User user, QueryOptionsSupplier q @Override public @NonNull Tristate getPermissionValue(@NonNull String permission) { - return CompatibilityUtil.convertTristate(this.user.getCachedData().getPermissionData(this.queryOptionsSupplier.getQueryOptions()).checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK).result()); + return CompatibilityUtil.convertTristate(this.user.getCachedData().getPermissionData(this.queryOptionsSupplier.getQueryOptions()).checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result()); } } From 419d711fb6c3aa96aad37dda407df3c9b5f79507 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 31 Dec 2021 11:16:06 -0500 Subject: [PATCH 053/314] Add full support for transient permissions through Vault API (#3258) --- .../bukkit/vault/AbstractVaultPermission.java | 72 +++++++++++++++++-- .../vault/LuckPermsVaultPermission.java | 41 ++++++++--- 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultPermission.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultPermission.java index e22b3a1b5..fdce348aa 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultPermission.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultPermission.java @@ -79,6 +79,8 @@ public final boolean hasGroupSupport() { public abstract boolean userHasPermission(String world, UUID uuid, String permission); public abstract boolean userAddPermission(String world, UUID uuid, String permission); public abstract boolean userRemovePermission(String world, UUID uuid, String permission); + public abstract boolean userAddTransient(String world, UUID uuid, String permission); + public abstract boolean userRemoveTransient(String world, UUID uuid, String permission); public abstract boolean userInGroup(String world, UUID uuid, String group); public abstract boolean userAddGroup(String world, UUID uuid, String group); public abstract boolean userRemoveGroup(String world, UUID uuid, String group); @@ -181,7 +183,7 @@ public final boolean playerAdd(String world, OfflinePlayer player, String permis public final boolean playerAdd(Player player, String permission) { Objects.requireNonNull(player, "player"); Objects.requireNonNull(permission, "permission"); - return userAddPermission(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), permission); + return userAddPermission(convertWorld(player), player.getUniqueId(), permission); } @Override @@ -209,7 +211,63 @@ public final boolean playerRemove(World world, String player, String permission) public final boolean playerRemove(Player player, String permission) { Objects.requireNonNull(player, "player"); Objects.requireNonNull(permission, "permission"); - return userRemovePermission(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), permission); + return userRemovePermission(convertWorld(player), player.getUniqueId(), permission); + } + + @Override + public boolean playerAddTransient(OfflinePlayer player, String permission) { + Objects.requireNonNull(player, "player"); + Objects.requireNonNull(permission, "permission"); + return userAddTransient(null, player.getUniqueId(), permission); + } + + @Override + public final boolean playerAddTransient(Player player, String permission) { + Objects.requireNonNull(player, "player"); + Objects.requireNonNull(permission, "permission"); + return userAddTransient(convertWorld(player), player.getUniqueId(), permission); + } + + @Override + public final boolean playerAddTransient(String world, OfflinePlayer player, String permission) { + Objects.requireNonNull(player, "player"); + Objects.requireNonNull(permission, "permission"); + return userAddTransient(convertWorld(world), player.getUniqueId(), permission); + } + + @Override + public final boolean playerAddTransient(String world, Player player, String permission) { + Objects.requireNonNull(player, "player"); + Objects.requireNonNull(permission, "permission"); + return userAddTransient(convertWorld(world), player.getUniqueId(), permission); + } + + @Override + public boolean playerRemoveTransient(OfflinePlayer player, String permission) { + Objects.requireNonNull(player, "player"); + Objects.requireNonNull(permission, "permission"); + return userRemoveTransient(null, player.getUniqueId(), permission); + } + + @Override + public final boolean playerRemoveTransient(Player player, String permission) { + Objects.requireNonNull(player, "player"); + Objects.requireNonNull(permission, "permission"); + return userRemoveTransient(convertWorld(player), player.getUniqueId(), permission); + } + + @Override + public final boolean playerRemoveTransient(String world, OfflinePlayer player, String permission) { + Objects.requireNonNull(player, "player"); + Objects.requireNonNull(permission, "permission"); + return userRemoveTransient(convertWorld(world), player.getUniqueId(), permission); + } + + @Override + public final boolean playerRemoveTransient(String world, Player player, String permission) { + Objects.requireNonNull(player, "player"); + Objects.requireNonNull(permission, "permission"); + return userRemoveTransient(convertWorld(world), player.getUniqueId(), permission); } @Override @@ -279,7 +337,7 @@ public final boolean playerInGroup(String world, OfflinePlayer player, String gr public final boolean playerInGroup(Player player, String group) { Objects.requireNonNull(player, "player"); Objects.requireNonNull(group, "group"); - return userInGroup(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), group); + return userInGroup(convertWorld(player), player.getUniqueId(), group); } @Override @@ -307,7 +365,7 @@ public final boolean playerAddGroup(String world, OfflinePlayer player, String g public final boolean playerAddGroup(Player player, String group) { Objects.requireNonNull(player, "player"); Objects.requireNonNull(group, "group"); - return userAddGroup(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), group); + return userAddGroup(convertWorld(player), player.getUniqueId(), group); } @Override @@ -334,7 +392,7 @@ public final boolean playerRemoveGroup(String world, OfflinePlayer player, Strin @Override public final boolean playerRemoveGroup(Player player, String group) { Objects.requireNonNull(player, "player"); - return userRemoveGroup(convertWorld(player), ((OfflinePlayer) player).getUniqueId(), group); + return userRemoveGroup(convertWorld(player), player.getUniqueId(), group); } @Override @@ -358,7 +416,7 @@ public final String[] getPlayerGroups(String world, OfflinePlayer player) { @Override public final String[] getPlayerGroups(Player player) { Objects.requireNonNull(player, "player"); - return userGetGroups(convertWorld(player), ((OfflinePlayer) player).getUniqueId()); + return userGetGroups(convertWorld(player), player.getUniqueId()); } @Override @@ -382,7 +440,7 @@ public final String getPrimaryGroup(String world, OfflinePlayer player) { @Override public final String getPrimaryGroup(Player player) { Objects.requireNonNull(player, "player"); - return userGetPrimaryGroup(convertWorld(player), ((OfflinePlayer) player).getUniqueId()); + return userGetPrimaryGroup(convertWorld(player), player.getUniqueId()); } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java index 8c189c5ac..5776a182a 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java @@ -186,7 +186,7 @@ public boolean userAddPermission(String world, UUID uuid, String permission) { if (user instanceof Group) { throw new UnsupportedOperationException("Unable to modify the permissions of NPC players"); } - return holderAddPermission(user, permission, world); + return holderAddPermission(user, permission, world, DataType.NORMAL); } @Override @@ -198,7 +198,31 @@ public boolean userRemovePermission(String world, UUID uuid, String permission) if (user instanceof Group) { throw new UnsupportedOperationException("Unable to modify the permissions of NPC players"); } - return holderRemovePermission(user, permission, world); + return holderRemovePermission(user, permission, world, DataType.NORMAL); + } + + @Override + public boolean userAddTransient(String world, UUID uuid, String permission) { + Objects.requireNonNull(uuid, "uuid"); + Objects.requireNonNull(permission, "permission"); + + PermissionHolder user = lookupUser(uuid); + if (user instanceof Group) { + throw new UnsupportedOperationException("Unable to modify the permissions of NPC players"); + } + return holderAddPermission(user, permission, world, DataType.TRANSIENT); + } + + @Override + public boolean userRemoveTransient(String world, UUID uuid, String permission) { + Objects.requireNonNull(uuid, "uuid"); + Objects.requireNonNull(permission, "permission"); + + PermissionHolder user = lookupUser(uuid); + if (user instanceof Group) { + throw new UnsupportedOperationException("Unable to modify the permissions of NPC players"); + } + return holderRemovePermission(user, permission, world, DataType.TRANSIENT); } @Override @@ -288,7 +312,7 @@ public boolean groupAddPermission(String world, String name, String permission) return false; } - return holderAddPermission(group, permission, world); + return holderAddPermission(group, permission, world, DataType.NORMAL); } @Override @@ -301,7 +325,7 @@ public boolean groupRemovePermission(String world, String name, String permissio return false; } - return holderRemovePermission(group, permission, world); + return holderRemovePermission(group, permission, world, DataType.NORMAL); } // utility methods for getting user and group instances @@ -387,7 +411,7 @@ QueryOptions getQueryOptions(@Nullable UUID uuid, @Nullable String world) { // utility methods for modifying the state of PermissionHolders - private boolean holderAddPermission(PermissionHolder holder, String permission, String world) { + private boolean holderAddPermission(PermissionHolder holder, String permission, String world, DataType type) { Objects.requireNonNull(permission, "permission is null"); Preconditions.checkArgument(!permission.isEmpty(), "permission is an empty string"); @@ -396,13 +420,13 @@ private boolean holderAddPermission(PermissionHolder holder, String permission, .withContext(DefaultContextKeys.WORLD_KEY, world == null ? "global" : world) .build(); - if (holder.setNode(DataType.NORMAL, node, true).wasSuccessful()) { + if (holder.setNode(type, node, true).wasSuccessful()) { return holderSave(holder); } return false; } - private boolean holderRemovePermission(PermissionHolder holder, String permission, String world) { + private boolean holderRemovePermission(PermissionHolder holder, String permission, String world, DataType type) { Objects.requireNonNull(permission, "permission is null"); Preconditions.checkArgument(!permission.isEmpty(), "permission is an empty string"); @@ -411,7 +435,7 @@ private boolean holderRemovePermission(PermissionHolder holder, String permissio .withContext(DefaultContextKeys.WORLD_KEY, world == null ? "global" : world) .build(); - if (holder.unsetNode(DataType.NORMAL, node).wasSuccessful()) { + if (holder.unsetNode(type, node).wasSuccessful()) { return holderSave(holder); } return false; @@ -460,4 +484,5 @@ boolean isIgnoreWorld() { private boolean useVaultServer() { return this.plugin.getConfiguration().get(ConfigKeys.USE_VAULT_SERVER); } + } From d319d8dc527dfc2c82292916d9e2fdf726d4188e Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 31 Dec 2021 17:24:38 +0000 Subject: [PATCH 054/314] Fix TristateResult NPE --- .../common/cacheddata/result/TristateResult.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java index bbdb0dd97..0df78b645 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java @@ -134,11 +134,17 @@ public TristateResult result(Tristate result) { } } - public TristateResult result(@NonNull Node node) { + public TristateResult result(@Nullable Node node) { + if (node == null) { + return UNDEFINED; + } return new TristateResult(Tristate.of(node.getValue()), node, this.processorClass); } - public TristateResult resultWithOverride(@NonNull Node node, Tristate result) { + public TristateResult resultWithOverride(@Nullable Node node, @NonNull Tristate result) { + if (result == Tristate.UNDEFINED) { + return UNDEFINED; + } return new TristateResult(result, node, this.processorClass); } } From acdc25977111088a77a5828717947bf72c40fdf9 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 31 Dec 2021 17:50:42 +0000 Subject: [PATCH 055/314] Fix MetaValueSelector NPE --- .../net/luckperms/api/cacheddata}/Result.java | 3 ++- .../api/query/meta/MetaValueSelector.java | 3 ++- .../cacheddata/result/StringResult.java | 1 + .../cacheddata/result/TristateResult.java | 1 + .../common/cacheddata/type/MetaCache.java | 5 +++-- .../type/SimpleMetaValueSelector.java | 22 +++++++++---------- .../common/verbose/VerboseListener.java | 2 +- .../common/verbose/event/VerboseEvent.java | 2 +- 8 files changed, 22 insertions(+), 17 deletions(-) rename {common/src/main/java/me/lucko/luckperms/common/cacheddata/result => api/src/main/java/net/luckperms/api/cacheddata}/Result.java (97%) diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/Result.java b/api/src/main/java/net/luckperms/api/cacheddata/Result.java similarity index 97% rename from common/src/main/java/me/lucko/luckperms/common/cacheddata/result/Result.java rename to api/src/main/java/net/luckperms/api/cacheddata/Result.java index be4294eea..0f61190f6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/Result.java +++ b/api/src/main/java/net/luckperms/api/cacheddata/Result.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.cacheddata.result; +package net.luckperms.api.cacheddata; import net.luckperms.api.node.Node; @@ -33,6 +33,7 @@ * Represents the result of a cached data lookup * * @param the result type + * @since 5.4 */ public interface Result { diff --git a/api/src/main/java/net/luckperms/api/query/meta/MetaValueSelector.java b/api/src/main/java/net/luckperms/api/query/meta/MetaValueSelector.java index a73eac171..c81170971 100644 --- a/api/src/main/java/net/luckperms/api/query/meta/MetaValueSelector.java +++ b/api/src/main/java/net/luckperms/api/query/meta/MetaValueSelector.java @@ -25,6 +25,7 @@ package net.luckperms.api.query.meta; +import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.query.OptionKey; @@ -54,6 +55,6 @@ public interface MetaValueSelector { * @param values the values, in the order in which they were accumulated. * @return the selected value */ - @NonNull MetaNode selectValue(@NonNull String key, @NonNull List values); + @NonNull Result selectValue(@NonNull String key, @NonNull List> values); } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java index 8efaff7f5..b8567a3b0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java @@ -25,6 +25,7 @@ package me.lucko.luckperms.common.cacheddata.result; +import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.Node; import net.luckperms.api.node.types.ChatMetaNode; import net.luckperms.api.node.types.MetaNode; diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java index 0df78b645..46886566e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java @@ -28,6 +28,7 @@ import me.lucko.luckperms.common.calculator.PermissionCalculator; import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; +import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.Node; import net.luckperms.api.util.Tristate; diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java index cfde15f96..066d1f03c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java @@ -40,6 +40,7 @@ import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.cacheddata.CachedMetaData; +import net.luckperms.api.cacheddata.Result; import net.luckperms.api.metastacking.MetaStackDefinition; import net.luckperms.api.node.types.ChatMetaNode; import net.luckperms.api.node.types.MetaNode; @@ -92,12 +93,12 @@ public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, MetaAccumula continue; } - MetaNode selected = metaValueSelector.selectValue(e.getKey(), Lists.transform(e.getValue(), StringResult::node)); + Result selected = metaValueSelector.selectValue(e.getKey(), e.getValue()); if (selected == null) { throw new NullPointerException(metaValueSelector + " returned null"); } - builder.put(e.getKey(), StringResult.of(selected)); + builder.put(e.getKey(), (StringResult) selected); } this.flattenedMeta = builder.build(); this.meta = new LowerCaseMetaMap(meta); diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java index c033dd41b..d8cdd1f14 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/SimpleMetaValueSelector.java @@ -25,6 +25,7 @@ package me.lucko.luckperms.common.cacheddata.type; +import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.query.meta.MetaValueSelector; @@ -44,7 +45,7 @@ public SimpleMetaValueSelector(Map strategies, Strategy defaul } @Override - public @NonNull MetaNode selectValue(@NonNull String key, @NonNull List values) { + public @NonNull Result selectValue(@NonNull String key, @NonNull List> values) { switch (values.size()) { case 0: throw new IllegalArgumentException("values is empty"); @@ -58,7 +59,7 @@ public SimpleMetaValueSelector(Map strategies, Strategy defaul public enum Strategy { INHERITANCE { @Override - public MetaNode select(List values) { + public Result select(List> values) { return values.get(0); } }, @@ -66,7 +67,7 @@ public MetaNode select(List values) { private final DoubleSelectionPredicate selection = (value, current) -> value > current; @Override - public MetaNode select(List values) { + public Result select(List> values) { return selectNumber(values, this.selection); } }, @@ -74,12 +75,12 @@ public MetaNode select(List values) { private final DoubleSelectionPredicate selection = (value, current) -> value < current; @Override - public MetaNode select(List values) { + public Result select(List> values) { return selectNumber(values, this.selection); } }; - public abstract MetaNode select(List values); + public abstract Result select(List> values); public static Strategy parse(String s) { try { @@ -95,16 +96,15 @@ private interface DoubleSelectionPredicate { boolean shouldSelect(double value, double current); } - private static MetaNode selectNumber(List values, DoubleSelectionPredicate selection) { + private static Result selectNumber(List> values, DoubleSelectionPredicate selection) { double current = 0; - MetaNode selected = null; + Result selected = null; - for (MetaNode node : values) { - String value = node.getMetaValue(); + for (Result result : values) { try { - double parse = Double.parseDouble(value); + double parse = Double.parseDouble(result.result()); if (selected == null || selection.shouldSelect(parse, current)) { - selected = node; + selected = result; current = parse; } } catch (NumberFormatException e) { diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java index ef7fd4acb..22c082368 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java @@ -27,7 +27,6 @@ import com.google.gson.JsonObject; -import me.lucko.luckperms.common.cacheddata.result.Result; import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.http.AbstractHttpClient; import me.lucko.luckperms.common.http.BytebinClient; @@ -47,6 +46,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.event.HoverEvent; +import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.Node; import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.query.QueryMode; diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java b/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java index 14f500e06..d1457d178 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/event/VerboseEvent.java @@ -27,13 +27,13 @@ import com.google.gson.JsonObject; -import me.lucko.luckperms.common.cacheddata.result.Result; import me.lucko.luckperms.common.util.StackTracePrinter; import me.lucko.luckperms.common.util.gson.JArray; import me.lucko.luckperms.common.util.gson.JObject; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; import me.lucko.luckperms.common.verbose.expression.BooleanExpressionCompiler.VariableEvaluator; +import net.luckperms.api.cacheddata.Result; import net.luckperms.api.context.Context; import net.luckperms.api.query.QueryMode; import net.luckperms.api.query.QueryOptions; From 6781c1fb519d34da55279eae35a9d00b94d6ce50 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 1 Jan 2022 14:38:46 +0000 Subject: [PATCH 056/314] Provide API access to cached data result info (#3243) --- .../luckperms/api/cacheddata/CachedData.java | 4 + .../api/cacheddata/CachedDataManager.java | 14 +-- .../api/cacheddata/CachedMetaData.java | 113 ++++++++++++++++-- .../api/cacheddata/CachedPermissionData.java | 27 ++++- .../net/luckperms/api/cacheddata/Result.java | 37 ++++-- .../api/cacheddata/package-info.java | 2 +- .../cacheddata/AbstractCachedDataManager.java | 17 +-- .../cacheddata/result/StringResult.java | 16 +-- .../cacheddata/result/TristateResult.java | 1 - .../cacheddata/type/MetaAccumulator.java | 22 ++-- .../common/cacheddata/type/MetaCache.java | 30 +++-- .../cacheddata/type/MetaStackAccumulator.java | 36 +++--- .../cacheddata/type/MonitoredMetaCache.java | 11 +- .../cacheddata/type/PermissionCache.java | 6 + 14 files changed, 239 insertions(+), 97 deletions(-) diff --git a/api/src/main/java/net/luckperms/api/cacheddata/CachedData.java b/api/src/main/java/net/luckperms/api/cacheddata/CachedData.java index 709ebff30..ce030d14c 100644 --- a/api/src/main/java/net/luckperms/api/cacheddata/CachedData.java +++ b/api/src/main/java/net/luckperms/api/cacheddata/CachedData.java @@ -31,6 +31,10 @@ /** * Holds cached lookup data for a given set of query options. + * + *

All calls will account for inheritance, as well as any default data + * provided by the platform. These calls are heavily cached and are therefore + * fast.

*/ public interface CachedData { diff --git a/api/src/main/java/net/luckperms/api/cacheddata/CachedDataManager.java b/api/src/main/java/net/luckperms/api/cacheddata/CachedDataManager.java index fe52d97a2..e1388b358 100644 --- a/api/src/main/java/net/luckperms/api/cacheddata/CachedDataManager.java +++ b/api/src/main/java/net/luckperms/api/cacheddata/CachedDataManager.java @@ -39,7 +39,7 @@ * Holds cached permission and meta lookup data for a {@link PermissionHolder}. * *

All calls will account for inheritance, as well as any default data - * provided by the platform. This calls are heavily cached and are therefore + * provided by the platform. These calls are heavily cached and are therefore * fast.

*/ public interface CachedDataManager { @@ -81,11 +81,10 @@ public interface CachedDataManager { *

For {@link User}s, the most appropriate query options will be their * {@link ContextManager#getQueryOptions(User) current active query options} if the * corresponding player is online, and otherwise, will fallback to - * {@link ContextManager#getStaticQueryOptions() the current static query options} - * if they are offline.

+ * {@link ContextManager#getStaticQueryOptions() the current static query options}.

* *

For {@link Group}s, the most appropriate query options will always be - * {@link ContextManager#getStaticQueryOptions()} the current static query options.

+ * {@link ContextManager#getStaticQueryOptions() the current static query options}.

* * @return a permission data instance * @since 5.1 @@ -99,11 +98,10 @@ public interface CachedDataManager { *

For {@link User}s, the most appropriate query options will be their * {@link ContextManager#getQueryOptions(User) current active query options} if the * corresponding player is online, and otherwise, will fallback to - * {@link ContextManager#getStaticQueryOptions() the current static query options} - * if they are offline.

+ * {@link ContextManager#getStaticQueryOptions() the current static query options}.

* *

For {@link Group}s, the most appropriate query options will always be - * {@link ContextManager#getStaticQueryOptions()} the current static query options.

+ * {@link ContextManager#getStaticQueryOptions() the current static query options}.

* * @return a meta data instance * @since 5.1 @@ -118,7 +116,7 @@ public interface CachedDataManager { void invalidate(); /** - * Invalidates all of the underlying Permission calculators. + * Invalidates all underlying permission calculators. * *

Can be called to allow for an update in defaults.

*/ diff --git a/api/src/main/java/net/luckperms/api/cacheddata/CachedMetaData.java b/api/src/main/java/net/luckperms/api/cacheddata/CachedMetaData.java index 646fb6f34..467d3c48c 100644 --- a/api/src/main/java/net/luckperms/api/cacheddata/CachedMetaData.java +++ b/api/src/main/java/net/luckperms/api/cacheddata/CachedMetaData.java @@ -26,6 +26,9 @@ package net.luckperms.api.cacheddata; import net.luckperms.api.metastacking.MetaStackDefinition; +import net.luckperms.api.node.types.MetaNode; +import net.luckperms.api.node.types.PrefixNode; +import net.luckperms.api.node.types.SuffixNode; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -39,16 +42,40 @@ /** * Holds cached meta lookup data for a specific set of contexts. + * + *

Meta data refers to {@link PrefixNode prefixes}, {@link SuffixNode suffixes} and + * {@link MetaNode meta (options)} held by a permission holder.

+ * + *

All calls will account for inheritance, as well as any default data + * provided by the platform. These calls are heavily cached and are therefore + * fast.

*/ public interface CachedMetaData extends CachedData { + /** + * Query a meta value for the given {@code key}. + * + *

This method will always return a {@link Result}, but the + * {@link Result#result() inner result} {@link String} will be null if a value + * for the given key was not found.

+ * + * @param key the key + * @return a result containing the value + * @since 5.4 + */ + @NonNull Result queryMetaValue(@NonNull String key); + /** * Gets a value for the given meta key. + * + *

If no such meta value exists for the given key, {@code null} is returned.

* * @param key the key * @return the value */ - @Nullable String getMetaValue(@NonNull String key); + default @Nullable String getMetaValue(@NonNull String key) { + return queryMetaValue(key).result(); + } /** * Gets a value for the given meta key, and runs it through the given {@code transformer}. @@ -81,37 +108,97 @@ public interface CachedMetaData extends CachedData { } /** - * Gets the holder's highest priority prefix, or null if the holder has no prefixes + * Query for a prefix. + * + *

This method uses the rules defined by the {@link #getPrefixStackDefinition() prefix stack} + * to produce a {@link String} output.

+ * + *

Assuming the default configuration is used, this will usually be the value of the + * holder's highest priority prefix node.

+ * + *

This method will always return a {@link Result}, but the + * {@link Result#result() inner result} {@link String} will be null if + * a the resultant prefix stack contained no elements.

+ * + * @return a result containing the prefix + * @since 5.4 + */ + @NonNull Result queryPrefix(); + + /** + * Gets the prefix. + * + *

This method uses the rules defined by the {@link #getPrefixStackDefinition() prefix stack} + * to produce a {@link String} output.

+ * + *

Assuming the default configuration is used, this will usually be the value of the + * holder's highest priority prefix node.

+ * + *

If the resultant prefix stack contained no elements, {@code null} is returned.

* * @return a prefix string, or null */ - @Nullable String getPrefix(); + default @Nullable String getPrefix() { + return queryPrefix().result(); + } + + /** + * Query for a suffix. + * + *

This method uses the rules defined by the {@link #getSuffixStackDefinition() suffix stack} + * to produce a {@link String} output.

+ * + *

Assuming the default configuration is used, this will usually be the value of the + * holder's highest priority suffix node.

+ * + *

This method will always return a {@link Result}, but the + * {@link Result#result() inner result} {@link String} will be null if + * a the resultant suffix stack contained no elements.

+ * + * @return a result containing the suffix + * @since 5.4 + */ + @NonNull Result querySuffix(); /** - * Gets the holder's highest priority suffix, or null if the holder has no suffixes + * Gets the suffix. + * + *

This method uses the rules defined by the {@link #getSuffixStackDefinition() suffix stack} + * to produce a {@link String} output.

+ * + *

Assuming the default configuration is used, this will usually be the value of the + * holder's highest priority suffix node.

+ * + *

If the resultant suffix stack contained no elements, {@code null} is returned.

* * @return a suffix string, or null */ - @Nullable String getSuffix(); + default @Nullable String getSuffix() { + return querySuffix().result(); + } /** - * Gets an immutable copy of the meta this holder has. + * Gets a map of all accumulated {@link MetaNode meta}. * - * @return an immutable map of meta + *

Prefer using the {@link #getMetaValue(String)} method for querying values.

+ * + * @return a map of meta */ @NonNull @Unmodifiable Map> getMeta(); /** - * Gets an immutable sorted map of all of the prefixes the holder has, whereby the first - * value is the highest priority prefix. + * Gets a sorted map of all accumulated {@link PrefixNode prefixes}. + * + *

Prefer using the {@link #getPrefix()} method for querying.

* * @return a sorted map of prefixes */ @NonNull @Unmodifiable SortedMap getPrefixes(); /** - * Gets an immutable sorted map of all of the suffixes the holder has, whereby the first - * value is the highest priority suffix. + * Gets a sorted map of all accumulated {@link SuffixNode suffixes}. + * + *

Prefer using the {@link #getSuffix()} method for querying.

* * @return a sorted map of suffixes */ @@ -128,14 +215,14 @@ public interface CachedMetaData extends CachedData { @Nullable String getPrimaryGroup(); /** - * Gets the definition used for the prefix stack + * Gets the definition used for the prefix stack. * * @return the definition used for the prefix stack */ @NonNull MetaStackDefinition getPrefixStackDefinition(); /** - * Gets the definition used for the suffix stack + * Gets the definition used for the suffix stack. * * @return the definition used for the suffix stack */ diff --git a/api/src/main/java/net/luckperms/api/cacheddata/CachedPermissionData.java b/api/src/main/java/net/luckperms/api/cacheddata/CachedPermissionData.java index 2b8e65aa8..c97c7ce94 100644 --- a/api/src/main/java/net/luckperms/api/cacheddata/CachedPermissionData.java +++ b/api/src/main/java/net/luckperms/api/cacheddata/CachedPermissionData.java @@ -25,6 +25,7 @@ package net.luckperms.api.cacheddata; +import net.luckperms.api.node.Node; import net.luckperms.api.util.Tristate; import org.checkerframework.checker.nullness.qual.NonNull; @@ -34,17 +35,39 @@ /** * Holds cached permission lookup data for a specific set of contexts. + * + *

All calls will account for inheritance, as well as any default data + * provided by the platform. These calls are heavily cached and are therefore + * fast.

*/ public interface CachedPermissionData extends CachedData { /** - * Gets a permission check result for the given permission node. + * Performs a permission check for the given {@code permission} node. + * + *

This check is equivalent to the "hasPermission" method call on most platforms. + * You can use {@link Tristate#asBoolean()} if you need a truthy result.

+ * + * @param permission the permission node + * @return a result containing the tristate + * @throws NullPointerException if permission is null + * @since 5.4 + */ + @NonNull Result queryPermission(@NonNull String permission); + + /** + * Performs a permission check for the given {@code permission} node. + * + *

This check is equivalent to the "hasPermission" method call on most platforms. + * You can use {@link Tristate#asBoolean()} if you need a truthy result.

* * @param permission the permission node * @return a tristate result * @throws NullPointerException if permission is null */ - @NonNull Tristate checkPermission(@NonNull String permission); + default @NonNull Tristate checkPermission(@NonNull String permission) { + return queryPermission(permission).result(); + } /** * Invalidates the underlying permission calculator cache. diff --git a/api/src/main/java/net/luckperms/api/cacheddata/Result.java b/api/src/main/java/net/luckperms/api/cacheddata/Result.java index 0f61190f6..9f7398f3d 100644 --- a/api/src/main/java/net/luckperms/api/cacheddata/Result.java +++ b/api/src/main/java/net/luckperms/api/cacheddata/Result.java @@ -30,9 +30,37 @@ import org.checkerframework.checker.nullness.qual.Nullable; /** - * Represents the result of a cached data lookup + * Represents the result of a cached data lookup. + * + *

You can find "the holder that has the node that caused this result" + * using the following code:

+ *

+ *
+ *
+ * public static {@link net.luckperms.api.model.PermissionHolder.Identifier} holderThatHasTheNodeThatCausedTheResult(Result<?, ?> result) {
+ *     {@link Node} node = result.node();
+ *     if (node == null) {
+ *         return null;
+ *     }
+ *     {@link net.luckperms.api.node.metadata.types.InheritanceOriginMetadata} origin = node.getMetadata(InheritanceOriginMetadata.KEY).orElse(null);
+ *     if (origin == null) {
+ *         return null;
+ *     }
+ *     return origin.getOrigin();
+ * }
+ * 
+ * + *

Combined with the node itself, this is all the information needed to determine + * the root cause of the result.

+ *
+ * + *

The nullability of {@link #result()} is purposely undefined to allow the + * flexibility for methods using {@link Result} to declare it. In general, if the {@code T} type + * has a nullable/undefined value, then the return of {@link #result()} will be non-null, + * and if not, it will be nullable.

* * @param the result type + * @param the node type * @since 5.4 */ public interface Result { @@ -51,11 +79,4 @@ public interface Result { */ @Nullable N node(); - /** - * Gets the result that this result overrides, if applicable. - * - * @return the overridden result - */ - @Nullable Result overriddenResult(); - } diff --git a/api/src/main/java/net/luckperms/api/cacheddata/package-info.java b/api/src/main/java/net/luckperms/api/cacheddata/package-info.java index 0358e6716..ec5b4bb8c 100644 --- a/api/src/main/java/net/luckperms/api/cacheddata/package-info.java +++ b/api/src/main/java/net/luckperms/api/cacheddata/package-info.java @@ -24,7 +24,7 @@ */ /** - * CachedData lookup API for {@link net.luckperms.api.model.user.User}s and + * Caches permission checks and meta lookups for {@link net.luckperms.api.model.user.User}s and * {@link net.luckperms.api.model.group.Group}s. */ package net.luckperms.api.cacheddata; \ No newline at end of file diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java index 581c11663..930db77fe 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java @@ -255,24 +255,11 @@ public void invalidate() { this.cache.clear(); } } - - private MetaStackDefinition getMetaStackDefinition(QueryOptions queryOptions, ChatMetaType type) { - MetaStackDefinition stack = queryOptions.option(type == ChatMetaType.PREFIX ? - MetaStackDefinition.PREFIX_STACK_KEY : - MetaStackDefinition.SUFFIX_STACK_KEY - ).orElse(null); - - if (stack == null) { - stack = getDefaultMetaStackDefinition(type); - } - - return stack; - } private MetaAccumulator newAccumulator(QueryOptions queryOptions) { return new MetaAccumulator( - getMetaStackDefinition(queryOptions, ChatMetaType.PREFIX), - getMetaStackDefinition(queryOptions, ChatMetaType.SUFFIX) + queryOptions.option(MetaStackDefinition.PREFIX_STACK_KEY).orElseGet(() -> getDefaultMetaStackDefinition(ChatMetaType.PREFIX)), + queryOptions.option(MetaStackDefinition.SUFFIX_STACK_KEY).orElseGet(() -> getDefaultMetaStackDefinition(ChatMetaType.SUFFIX)) ); } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java index b8567a3b0..a88a9148c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java @@ -62,7 +62,6 @@ public StringResult(String result, N node, StringResult overriddenResult) { return this.node; } - @Override public @Nullable StringResult overriddenResult() { return this.overriddenResult; } @@ -86,23 +85,24 @@ public String toString() { private static final StringResult NULL_RESULT = new StringResult<>(null, null, null); @SuppressWarnings("unchecked") - public static StringResult nullResult() { - return (StringResult) NULL_RESULT; + public static StringResult nullResult() { + return (StringResult) NULL_RESULT; } - public static StringResult of(String result) { + public static StringResult of(String result) { return new StringResult<>(result, null, null); } + public static StringResult of(String result, N node) { + return new StringResult<>(result, node, null); + } + public static StringResult of(MetaNode node) { return new StringResult<>(node.getMetaValue(), node, null); } - public static StringResult> of(ChatMetaNode node) { + public static > StringResult of(N node) { return new StringResult<>(node.getMetaValue(), node, null); } - public static StringResult> of(String result, ChatMetaNode node) { - return new StringResult<>(result, node, null); - } } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java index 46886566e..f8a8a49f6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java @@ -81,7 +81,6 @@ private TristateResult(Tristate result, Node node, Class state = new AtomicReference<>(State.ACCUMULATING); private final ListMultimap> meta; - private final SortedMap>> prefixes; - private final SortedMap>> suffixes; + private final SortedMap> prefixes; + private final SortedMap> suffixes; private int weight = 0; private String primaryGroup; @@ -87,8 +87,8 @@ private enum State { private final MetaStackDefinition prefixDefinition; private final MetaStackDefinition suffixDefinition; - private final MetaStackAccumulator prefixAccumulator; - private final MetaStackAccumulator suffixAccumulator; + private final MetaStackAccumulator prefixAccumulator; + private final MetaStackAccumulator suffixAccumulator; public MetaAccumulator(MetaStackDefinition prefixDefinition, MetaStackDefinition suffixDefinition) { Objects.requireNonNull(prefixDefinition, "prefixDefinition"); @@ -98,8 +98,8 @@ public MetaAccumulator(MetaStackDefinition prefixDefinition, MetaStackDefinition this.suffixes = new TreeMap<>(Comparator.reverseOrder()); this.prefixDefinition = prefixDefinition; this.suffixDefinition = suffixDefinition; - this.prefixAccumulator = new MetaStackAccumulator(this.prefixDefinition, ChatMetaType.PREFIX); - this.suffixAccumulator = new MetaStackAccumulator(this.suffixDefinition, ChatMetaType.SUFFIX); + this.prefixAccumulator = new MetaStackAccumulator<>(this.prefixDefinition, ChatMetaType.PREFIX); + this.suffixAccumulator = new MetaStackAccumulator<>(this.suffixDefinition, ChatMetaType.SUFFIX); } private void ensureState(State state) { @@ -181,17 +181,17 @@ public ListMultimap> getMeta() { return this.meta; } - public Map>> getChatMeta(ChatMetaType type) { + public Map>> getChatMeta(ChatMetaType type) { ensureState(State.COMPLETE); return type == ChatMetaType.PREFIX ? this.prefixes : this.suffixes; } - public SortedMap>> getPrefixes() { + public SortedMap> getPrefixes() { ensureState(State.COMPLETE); return this.prefixes; } - public SortedMap>> getSuffixes() { + public SortedMap> getSuffixes() { ensureState(State.COMPLETE); return this.suffixes; } @@ -216,12 +216,12 @@ public MetaStackDefinition getSuffixDefinition() { return this.suffixDefinition; } - public StringResult> getPrefix() { + public StringResult getPrefix() { ensureState(State.COMPLETE); return this.prefixAccumulator.toResult(); } - public StringResult> getSuffix() { + public StringResult getSuffix() { ensureState(State.COMPLETE); return this.suffixAccumulator.toResult(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java index 066d1f03c..da8f7ce23 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java @@ -42,8 +42,9 @@ import net.luckperms.api.cacheddata.CachedMetaData; import net.luckperms.api.cacheddata.Result; import net.luckperms.api.metastacking.MetaStackDefinition; -import net.luckperms.api.node.types.ChatMetaNode; import net.luckperms.api.node.types.MetaNode; +import net.luckperms.api.node.types.PrefixNode; +import net.luckperms.api.node.types.SuffixNode; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.query.meta.MetaValueSelector; @@ -69,14 +70,14 @@ public class MetaCache extends UsageTracked implements CachedMetaData { /* The data */ private final Map>> meta; private final Map> flattenedMeta; - private final SortedMap>> prefixes; - private final SortedMap>> suffixes; + private final SortedMap> prefixes; + private final SortedMap> suffixes; private final int weight; private final String primaryGroup; private final MetaStackDefinition prefixDefinition; private final MetaStackDefinition suffixDefinition; - private final StringResult> prefix; - private final StringResult> suffix; + private final StringResult prefix; + private final StringResult suffix; public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, MetaAccumulator sourceMeta) { this.plugin = plugin; @@ -118,24 +119,39 @@ public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, MetaAccumula return this.flattenedMeta.getOrDefault(key.toLowerCase(Locale.ROOT), StringResult.nullResult()); } + @Override + public final @NonNull Result queryMetaValue(@NonNull String key) { + return getMetaValue(key, CheckOrigin.LUCKPERMS_API); + } + @Override public final @Nullable String getMetaValue(@NonNull String key) { return getMetaValue(key, CheckOrigin.LUCKPERMS_API).result(); } - public @NonNull StringResult> getPrefix(CheckOrigin origin) { + public @NonNull StringResult getPrefix(CheckOrigin origin) { return this.prefix; } + @Override + public final @NonNull Result queryPrefix() { + return getPrefix(CheckOrigin.LUCKPERMS_API); + } + @Override public final @Nullable String getPrefix() { return getPrefix(CheckOrigin.LUCKPERMS_API).result(); } - public @NonNull StringResult> getSuffix(CheckOrigin origin) { + public @NonNull StringResult getSuffix(CheckOrigin origin) { return this.suffix; } + @Override + public final @NonNull Result querySuffix() { + return getSuffix(CheckOrigin.LUCKPERMS_API); + } + @Override public final @Nullable String getSuffix() { return getSuffix(CheckOrigin.LUCKPERMS_API).result(); diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaStackAccumulator.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaStackAccumulator.java index 124dd7ca1..ad7eb88a6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaStackAccumulator.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaStackAccumulator.java @@ -41,9 +41,9 @@ import java.util.Objects; import java.util.stream.Collectors; -public class MetaStackAccumulator { +public class MetaStackAccumulator> { private final MetaStackDefinition definition; - private final List entries; + private final List> entries; public MetaStackAccumulator(MetaStackDefinition definition, ChatMetaType targetType) { this.definition = definition; @@ -51,17 +51,17 @@ public MetaStackAccumulator(MetaStackDefinition definition, ChatMetaType targetT List elements = definition.getElements(); this.entries = new ArrayList<>(elements.size()); for (MetaStackElement element : elements) { - this.entries.add(new Entry(element, targetType)); + this.entries.add(new Entry<>(element, targetType)); } } - public void offer(ChatMetaNode node) { - for (Entry entry : this.entries) { + public void offer(N node) { + for (Entry entry : this.entries) { entry.offer(node); } } - public List> getElements() { + public List getElements() { return this.entries.stream() .map(Entry::getNode) .filter(Objects::nonNull) @@ -70,8 +70,8 @@ public void offer(ChatMetaNode node) { public String toFormattedString() { List elements = new LinkedList<>(); - for (Entry entry : this.entries) { - ChatMetaNode node = entry.getNode(); + for (Entry entry : this.entries) { + N node = entry.getNode(); if (node != null) { elements.add(node.getMetaValue()); } @@ -105,13 +105,13 @@ public String toFormattedString() { return sb.toString(); } - public StringResult> toResult() { + public StringResult toResult() { String formatted = toFormattedString(); if (formatted == null) { return StringResult.nullResult(); } - List> elements = getElements(); + List elements = getElements(); switch (elements.size()) { case 0: @@ -119,12 +119,12 @@ public String toFormattedString() { case 1: return StringResult.of(formatted, elements.get(0)); default: { - Iterator> it = elements.iterator(); - StringResult> result = StringResult.of(formatted, it.next()); + Iterator it = elements.iterator(); + StringResult result = StringResult.of(formatted, it.next()); - StringResult> root = result; + StringResult root = result; while (it.hasNext()) { - StringResult> nested = StringResult.of(it.next()); + StringResult nested = StringResult.of(it.next()); root.setOverriddenResult(nested); root = nested; } @@ -134,22 +134,22 @@ public String toFormattedString() { } } - private static final class Entry { + private static final class Entry> { private final MetaStackElement element; private final ChatMetaType type; - private @Nullable ChatMetaNode current = null; + private @Nullable N current = null; Entry(MetaStackElement element, ChatMetaType type) { this.element = element; this.type = type; } - public ChatMetaNode getNode() { + public N getNode() { return this.current; } - public boolean offer(ChatMetaNode node) { + public boolean offer(N node) { if (this.element.shouldAccumulate(this.type, node, this.current)) { this.current = node; return true; diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java index fd014eecd..ecd2116c6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java @@ -35,8 +35,9 @@ import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.cacheddata.CachedMetaData; -import net.luckperms.api.node.types.ChatMetaNode; import net.luckperms.api.node.types.MetaNode; +import net.luckperms.api.node.types.PrefixNode; +import net.luckperms.api.node.types.SuffixNode; import net.luckperms.api.query.QueryOptions; import org.checkerframework.checker.nullness.qual.NonNull; @@ -73,16 +74,16 @@ public StringResult getMetaValue(String key, CheckOrigin origin) { @Override @NonNull - public StringResult> getPrefix(CheckOrigin origin) { - StringResult> value = super.getPrefix(origin); + public StringResult getPrefix(CheckOrigin origin) { + StringResult value = super.getPrefix(origin); this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Prefix.NODE_KEY, value); return value; } @Override @NonNull - public StringResult> getSuffix(CheckOrigin origin) { - StringResult> value = super.getSuffix(origin); + public StringResult getSuffix(CheckOrigin origin) { + StringResult value = super.getSuffix(origin); this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Suffix.NODE_KEY, value); return value; } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/PermissionCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/PermissionCache.java index c26d5a4c4..6b754579e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/PermissionCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/PermissionCache.java @@ -35,6 +35,7 @@ import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.cacheddata.CachedPermissionData; +import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.Node; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; @@ -102,6 +103,11 @@ public TristateResult checkPermission(String permission, CheckOrigin origin) { return this.calculator.checkPermission(permission, origin); } + @Override + public @NonNull Result queryPermission(@NonNull String permission) { + return checkPermission(permission, CheckOrigin.LUCKPERMS_API); + } + @Override public @NonNull Tristate checkPermission(@NonNull String permission) { return checkPermission(permission, CheckOrigin.LUCKPERMS_API).result(); From 4de7448394e9364c4a70c7e5096a4584da4ed215 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 1 Jan 2022 19:33:48 +0000 Subject: [PATCH 057/314] Include whether a node is normal/transient in InheritanceOriginMetadata --- .../node/metadata/types/InheritanceOriginMetadata.java | 10 ++++++++++ .../luckperms/common/model/InheritanceOrigin.java | 10 +++++++++- .../lucko/luckperms/common/model/PermissionHolder.java | 4 ++-- .../luckperms/common/model/nodemap/NodeMapMutable.java | 7 +++++-- .../model/calculated/CalculatedSubjectData.java | 4 ++-- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/net/luckperms/api/node/metadata/types/InheritanceOriginMetadata.java b/api/src/main/java/net/luckperms/api/node/metadata/types/InheritanceOriginMetadata.java index 68b70f95b..ec318fae5 100644 --- a/api/src/main/java/net/luckperms/api/node/metadata/types/InheritanceOriginMetadata.java +++ b/api/src/main/java/net/luckperms/api/node/metadata/types/InheritanceOriginMetadata.java @@ -26,6 +26,7 @@ package net.luckperms.api.node.metadata.types; import net.luckperms.api.model.PermissionHolder; +import net.luckperms.api.model.data.DataType; import net.luckperms.api.node.Node; import net.luckperms.api.node.metadata.NodeMetadataKey; @@ -56,6 +57,15 @@ public interface InheritanceOriginMetadata { */ PermissionHolder.@NonNull Identifier getOrigin(); + /** + * Gets the {@link DataType type} of the {@link net.luckperms.api.model.data.NodeMap} + * the node was inherited from. + * + * @return the type of the NodeMap the node was inherited from. + * @since 5.4 + */ + @NonNull DataType getDataType(); + /** * Gets whether the associated node was inherited from another holder. * diff --git a/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java b/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java index d9acea639..5dbd596ca 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java @@ -26,19 +26,27 @@ package me.lucko.luckperms.common.model; import net.luckperms.api.model.PermissionHolder; +import net.luckperms.api.model.data.DataType; import net.luckperms.api.node.metadata.types.InheritanceOriginMetadata; import org.checkerframework.checker.nullness.qual.NonNull; public class InheritanceOrigin implements InheritanceOriginMetadata { private final PermissionHolder.Identifier location; + private final DataType dataType; - public InheritanceOrigin(PermissionHolder.Identifier location) { + public InheritanceOrigin(PermissionHolder.Identifier location, DataType dataType) { this.location = location; + this.dataType = dataType; } @Override public PermissionHolder.@NonNull Identifier getOrigin() { return this.location; } + + @Override + public @NonNull DataType getDataType() { + return this.dataType; + } } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index 948d1b1e7..d5577e7f2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -111,7 +111,7 @@ public abstract class PermissionHolder { * * @see #normalData() */ - private final RecordedNodeMap normalNodes = new RecordedNodeMap(new NodeMapMutable(this)); + private final RecordedNodeMap normalNodes = new RecordedNodeMap(new NodeMapMutable(this, DataType.NORMAL)); /** * The holders transient nodes. @@ -123,7 +123,7 @@ public abstract class PermissionHolder { * * @see #transientData() */ - private final NodeMap transientNodes = new NodeMapMutable(this); + private final NodeMap transientNodes = new NodeMapMutable(this, DataType.TRANSIENT); /** * Comparator used to ordering groups when calculating inheritance diff --git a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java index 8ec1d828e..7084c3cd5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java @@ -35,6 +35,7 @@ import net.luckperms.api.context.ContextSatisfyMode; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.ImmutableContextSet; +import net.luckperms.api.model.data.DataType; import net.luckperms.api.node.Node; import net.luckperms.api.node.NodeEqualityPredicate; import net.luckperms.api.node.metadata.types.InheritanceOriginMetadata; @@ -89,9 +90,11 @@ private static SortedMap> cre private final Lock lock = new ReentrantLock(); protected final PermissionHolder holder; + private final DataType type; - public NodeMapMutable(PermissionHolder holder) { + public NodeMapMutable(PermissionHolder holder, DataType type) { this.holder = holder; + this.type = type; } @Override @@ -115,7 +118,7 @@ private Node addInheritanceOrigin(Node node) { return node; } - return node.toBuilder().withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.holder.getIdentifier())).build(); + return node.toBuilder().withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.holder.getIdentifier(), this.type)).build(); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java index 9637e2160..fe8ff8076 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java @@ -155,7 +155,7 @@ public Map resolvePermissions(QueryOptions filter) { .permission(key) .value(value) .context(entry.getKey()) - .withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.parentSubject.getIdentifier())) + .withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.parentSubject.getIdentifier(), this.type)) .build(); nodeMap.put(key, node); }); @@ -320,7 +320,7 @@ public Map resolveOptions(QueryOptions filter) { .key(key) .value(value) .context(entry.getKey()) - .withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.parentSubject.getIdentifier())) + .withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.parentSubject.getIdentifier(), this.type)) .build(); nodeMap.put(key, node); }); From a53c9fad003bd39a0c502e8324c6b9846af15de4 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 1 Jan 2022 20:49:12 +0000 Subject: [PATCH 058/314] Reuse the same InheritanceOrigin object where possible --- .../generic/parent/ParentAddTemp.java | 2 +- .../luckperms/common/locale/Message.java | 8 ++--- .../lucko/luckperms/common/model/Group.java | 9 ++---- .../common/model/InheritanceOrigin.java | 31 +++++++++++++++---- .../common/model/PermissionHolder.java | 21 +++---------- .../model/PermissionHolderIdentifier.java | 22 ++++++++----- .../me/lucko/luckperms/common/model/User.java | 7 +---- .../luckperms/common/model/WeightCache.java | 2 +- .../common/model/nodemap/NodeMapMutable.java | 10 +++--- .../common/webeditor/WebEditorRequest.java | 2 +- .../sponge/service/model/LPSubject.java | 5 +-- .../calculated/CalculatedSubjectData.java | 6 ++-- .../model/permissionholder/GroupSubject.java | 5 ++- .../model/permissionholder/UserSubject.java | 5 ++- .../model/persisted/PersistedSubject.java | 3 +- 15 files changed, 70 insertions(+), 68 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java index bdb77c54a..40770eaed 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java @@ -83,7 +83,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, PermissionHolder targ return; } - if (group.getName().equalsIgnoreCase(target.getObjectName())) { + if (group.getName().equalsIgnoreCase(target.getIdentifier().getName())) { Message.ALREADY_TEMP_INHERITS.send(sender, target, group, context); return; } diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index 9ddf461c3..5f340c5a2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -1671,7 +1671,7 @@ static TextComponent prefixed(ComponentLike component) { .append(space()) .append(formatContextSetBracketed(node.getContexts(), empty())) .apply(builder -> { - String holderName = holder.getType() == HolderType.GROUP ? holder.getObjectName() : holder.getPlainDisplayName(); + String holderName = holder.getType() == HolderType.GROUP ? holder.getIdentifier().getName() : holder.getPlainDisplayName(); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); Component hover = join(newline(), @@ -1701,7 +1701,7 @@ static TextComponent prefixed(ComponentLike component) { .append(space()) .append(formatContextSetBracketed(node.getContexts(), empty())) .apply(builder -> { - String holderName = holder.getType() == HolderType.GROUP ? holder.getObjectName() : holder.getPlainDisplayName(); + String holderName = holder.getType() == HolderType.GROUP ? holder.getIdentifier().getName() : holder.getPlainDisplayName(); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); Component hover = join(newline(), @@ -1771,7 +1771,7 @@ static TextComponent prefixed(ComponentLike component) { .content(node.getGroupName()) .color(GREEN) .apply(builder -> { - String holderName = holder.getType() == HolderType.GROUP ? holder.getObjectName() : holder.getPlainDisplayName(); + String holderName = holder.getType() == HolderType.GROUP ? holder.getIdentifier().getName() : holder.getPlainDisplayName(); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); Component hover = join(newline(), @@ -1804,7 +1804,7 @@ static TextComponent prefixed(ComponentLike component) { .content(node.getGroupName()) .color(GREEN) .apply(builder -> { - String holderName = holder.getType() == HolderType.GROUP ? holder.getObjectName() : holder.getPlainDisplayName(); + String holderName = holder.getType() == HolderType.GROUP ? holder.getIdentifier().getName() : holder.getPlainDisplayName(); boolean explicitGlobalContext = !holder.getPlugin().getConfiguration().getContextsFile().getDefaultContexts().isEmpty(); Component hover = join(newline(), diff --git a/common/src/main/java/me/lucko/luckperms/common/model/Group.java b/common/src/main/java/me/lucko/luckperms/common/model/Group.java index 2814b8d80..54ff97955 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/Group.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/Group.java @@ -68,8 +68,8 @@ public class Group extends PermissionHolder { private final GroupCachedDataManager cachedData; public Group(String name, LuckPermsPlugin plugin) { - super(plugin); - this.name = name.toLowerCase(Locale.ROOT); + super(plugin, name.toLowerCase(Locale.ROOT)); + this.name = getIdentifier().getName(); this.cachedData = new GroupCachedDataManager(this); getPlugin().getEventDispatcher().dispatchGroupCacheLoad(this, this.cachedData); @@ -89,11 +89,6 @@ public String getName() { return this.name; } - @Override - public String getObjectName() { - return this.name; - } - public ApiGroup getApiProxy() { return this.apiProxy; } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java b/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java index 5dbd596ca..8fbe8f33c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java @@ -25,28 +25,47 @@ package me.lucko.luckperms.common.model; -import net.luckperms.api.model.PermissionHolder; import net.luckperms.api.model.data.DataType; import net.luckperms.api.node.metadata.types.InheritanceOriginMetadata; import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Objects; + public class InheritanceOrigin implements InheritanceOriginMetadata { - private final PermissionHolder.Identifier location; + private final PermissionHolderIdentifier origin; private final DataType dataType; - public InheritanceOrigin(PermissionHolder.Identifier location, DataType dataType) { - this.location = location; + public InheritanceOrigin(PermissionHolderIdentifier origin, DataType dataType) { + this.origin = origin; this.dataType = dataType; } @Override - public PermissionHolder.@NonNull Identifier getOrigin() { - return this.location; + public @NonNull PermissionHolderIdentifier getOrigin() { + return this.origin; } @Override public @NonNull DataType getDataType() { return this.dataType; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof InheritanceOriginMetadata)) return false; + InheritanceOriginMetadata that = (InheritanceOriginMetadata) o; + return this.origin.equals(that.getOrigin()) && this.dataType == that.getDataType(); + } + + @Override + public int hashCode() { + return Objects.hash(this.origin, getDataType()); + } + + @Override + public String toString() { + return this.origin + " (" + this.dataType + ")"; + } } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index d5577e7f2..fd9e71d4c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -53,7 +53,6 @@ import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -102,7 +101,7 @@ public abstract class PermissionHolder { /** * The holders identifier */ - private @MonotonicNonNull PermissionHolderIdentifier identifier; + private final PermissionHolderIdentifier identifier; /** * The holders persistent nodes. @@ -135,8 +134,9 @@ public abstract class PermissionHolder { * * @param plugin the plugin instance */ - protected PermissionHolder(LuckPermsPlugin plugin) { + protected PermissionHolder(LuckPermsPlugin plugin, String objectName) { this.plugin = plugin; + this.identifier = new PermissionHolderIdentifier(getType(), objectName); } // getters @@ -169,22 +169,9 @@ public NodeMap transientData() { } public PermissionHolderIdentifier getIdentifier() { - if (this.identifier == null) { - this.identifier = new PermissionHolderIdentifier(getType(), getObjectName()); - } return this.identifier; } - /** - * Gets the unique name of this holder object. - * - *

Used as a base for identifying permission holding objects. Also acts - * as a method for preventing circular inheritance issues.

- * - * @return the object name - */ - public abstract String getObjectName(); - /** * Gets the formatted display name of this permission holder * (for use in commands, etc) @@ -439,7 +426,7 @@ private boolean auditTemporaryNodes(DataType dataType) { } public Tristate hasNode(DataType type, Node node, NodeEqualityPredicate equalityPredicate) { - if (this.getType() == HolderType.GROUP && node instanceof InheritanceNode && ((InheritanceNode) node).getGroupName().equalsIgnoreCase(getObjectName())) { + if (this.getType() == HolderType.GROUP && node instanceof InheritanceNode && ((InheritanceNode) node).getGroupName().equalsIgnoreCase(getIdentifier().getName())) { return Tristate.TRUE; } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java index c8d58f720..dc829ff69 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java @@ -36,7 +36,9 @@ public final class PermissionHolderIdentifier implements Identifier { private final String name; public PermissionHolderIdentifier(HolderType type, String name) { - this.type = type == HolderType.USER ? Identifier.USER_TYPE : Identifier.GROUP_TYPE; + this.type = type == HolderType.USER + ? Identifier.USER_TYPE + : Identifier.GROUP_TYPE; this.name = name; } @@ -46,13 +48,13 @@ public PermissionHolderIdentifier(String type, String name) { } @Override - public @NonNull String getName() { - return this.name; + public @NonNull String getType() { + return this.type; } @Override - public @NonNull String getType() { - return this.type; + public @NonNull String getName() { + return this.name; } @Override @@ -60,12 +62,16 @@ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Identifier)) return false; Identifier that = (Identifier) o; - return getType().equals(that.getType()) && - getName().equals(that.getName()); + return this.type.equals(that.getType()) && this.name.equals(that.getName()); } @Override public int hashCode() { - return Objects.hash(getType(), getName()); + return Objects.hash(this.type, this.name); + } + + @Override + public String toString() { + return this.type + '/' + this.name; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/User.java b/common/src/main/java/me/lucko/luckperms/common/model/User.java index 24ce90573..879624bad 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/User.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/User.java @@ -62,7 +62,7 @@ public class User extends PermissionHolder { private final UserCachedDataManager cachedData; public User(UUID uniqueId, LuckPermsPlugin plugin) { - super(plugin); + super(plugin, uniqueId.toString()); this.uniqueId = uniqueId; this.primaryGroup = plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION).apply(this); this.cachedData = new UserCachedDataManager(this); @@ -77,11 +77,6 @@ public Optional getUsername() { return Optional.ofNullable(this.username); } - @Override - public String getObjectName() { - return this.uniqueId.toString(); - } - @Override public Component getFormattedDisplayName() { return Component.text(getPlainDisplayName()); diff --git a/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java b/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java index 48613caa5..e261c36f4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java @@ -63,7 +63,7 @@ public WeightCache(Group group) { if (!seen) { Map configWeights = this.group.getPlugin().getConfiguration().get(ConfigKeys.GROUP_WEIGHTS); - Integer value = configWeights.get(this.group.getObjectName().toLowerCase(Locale.ROOT)); + Integer value = configWeights.get(this.group.getIdentifier().getName().toLowerCase(Locale.ROOT)); if (value != null) { seen = true; weight = value; diff --git a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java index 7084c3cd5..97db73846 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java @@ -90,11 +90,11 @@ private static SortedMap> cre private final Lock lock = new ReentrantLock(); protected final PermissionHolder holder; - private final DataType type; + private final InheritanceOrigin inheritanceOrigin; public NodeMapMutable(PermissionHolder holder, DataType type) { this.holder = holder; - this.type = type; + this.inheritanceOrigin = new InheritanceOrigin(holder.getIdentifier(), type); } @Override @@ -113,12 +113,12 @@ protected ContextSatisfyMode defaultSatisfyMode() { } private Node addInheritanceOrigin(Node node) { - Optional metadata = node.getMetadata(InheritanceOriginMetadata.KEY); - if (metadata.isPresent() && metadata.get().getOrigin().equals(this.holder.getIdentifier())) { + Optional existing = node.getMetadata(InheritanceOriginMetadata.KEY); + if (existing.isPresent() && existing.get().equals(this.inheritanceOrigin)) { return node; } - return node.toBuilder().withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.holder.getIdentifier(), this.type)).build(); + return node.toBuilder().withMetadata(InheritanceOriginMetadata.KEY, this.inheritanceOrigin).build(); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java index ab4a75a2f..0a7ce368f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java @@ -139,7 +139,7 @@ private static JObject formMetadata(Sender sender, String cmdLabel, String plugi private static JObject formPermissionHolder(PermissionHolder holder) { return new JObject() .add("type", holder.getType().toString()) - .add("id", holder.getObjectName()) + .add("id", holder.getIdentifier().getName()) .add("displayName", holder.getPlainDisplayName()) .add("nodes", NodeJsonSerializer.serializeNodes(holder.normalData().asList())); } diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java index 41df403a6..9713d238e 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java @@ -27,8 +27,9 @@ import com.google.common.collect.ImmutableList; +import me.lucko.luckperms.common.model.PermissionHolderIdentifier; + import net.luckperms.api.context.ImmutableContextSet; -import net.luckperms.api.model.PermissionHolder; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; @@ -46,7 +47,7 @@ public interface LPSubject { LPPermissionService getService(); - PermissionHolder.Identifier getIdentifier(); + PermissionHolderIdentifier getIdentifier(); default LPSubjectReference toReference() { return getService().getReferenceFactory().obtain(this); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java index fe8ff8076..ea688e7a0 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectData.java @@ -70,6 +70,7 @@ public class CalculatedSubjectData implements LPSubjectData { private final LPSubject parentSubject; private final DataType type; + private final InheritanceOrigin inheritanceOrigin; private final LPPermissionService service; private final Map> permissions = new ConcurrentHashMap<>(); @@ -79,6 +80,7 @@ public class CalculatedSubjectData implements LPSubjectData { public CalculatedSubjectData(LPSubject parentSubject, DataType type, LPPermissionService service) { this.parentSubject = parentSubject; this.type = type; + this.inheritanceOrigin = new InheritanceOrigin(this.parentSubject.getIdentifier(), this.type); this.service = service; } @@ -155,7 +157,7 @@ public Map resolvePermissions(QueryOptions filter) { .permission(key) .value(value) .context(entry.getKey()) - .withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.parentSubject.getIdentifier(), this.type)) + .withMetadata(InheritanceOriginMetadata.KEY, this.inheritanceOrigin) .build(); nodeMap.put(key, node); }); @@ -320,7 +322,7 @@ public Map resolveOptions(QueryOptions filter) { .key(key) .value(value) .context(entry.getKey()) - .withMetadata(InheritanceOriginMetadata.KEY, new InheritanceOrigin(this.parentSubject.getIdentifier(), this.type)) + .withMetadata(InheritanceOriginMetadata.KEY, this.inheritanceOrigin) .build(); nodeMap.put(key, node); }); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java index b3fa83b37..00113d9db 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java @@ -25,13 +25,12 @@ package me.lucko.luckperms.sponge.service.model.permissionholder; +import me.lucko.luckperms.common.model.PermissionHolderIdentifier; import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.model.SpongeGroup; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; -import net.luckperms.api.model.PermissionHolder; - import org.spongepowered.api.command.CommandSource; import java.util.Optional; @@ -45,7 +44,7 @@ public GroupSubject(LPSpongePlugin plugin, SpongeGroup parent) { } @Override - public PermissionHolder.Identifier getIdentifier() { + public PermissionHolderIdentifier getIdentifier() { return this.parent.getIdentifier(); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java index bc6fa3fb4..4706033d2 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java @@ -25,13 +25,12 @@ package me.lucko.luckperms.sponge.service.model.permissionholder; +import me.lucko.luckperms.common.model.PermissionHolderIdentifier; import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.model.SpongeUser; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; -import net.luckperms.api.model.PermissionHolder; - import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandSource; @@ -48,7 +47,7 @@ public UserSubject(LPSpongePlugin plugin, SpongeUser parent) { } @Override - public PermissionHolder.Identifier getIdentifier() { + public PermissionHolderIdentifier getIdentifier() { return this.parent.getIdentifier(); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java index 6c6675a71..9fb681159 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java @@ -38,7 +38,6 @@ import me.lucko.luckperms.sponge.service.model.calculated.CalculatedSubjectData; import me.lucko.luckperms.sponge.service.model.calculated.MonitoredSubjectData; -import net.luckperms.api.model.PermissionHolder; import net.luckperms.api.model.data.DataType; import org.spongepowered.api.command.CommandSource; @@ -158,7 +157,7 @@ public ProxiedSubject sponge() { } @Override - public PermissionHolder.Identifier getIdentifier() { + public PermissionHolderIdentifier getIdentifier() { return this.identifier; } From f61d9ff9f0f8e63f4fab6c194e4621f6747d6585 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 2 Jan 2022 16:58:15 +0000 Subject: [PATCH 059/314] Fix NPE from permission holder initialisation order (#3263) --- .../lucko/luckperms/common/model/InheritanceOrigin.java | 4 ++-- .../lucko/luckperms/common/model/PermissionHolder.java | 9 ++++++--- .../common/model/PermissionHolderIdentifier.java | 8 ++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java b/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java index 8fbe8f33c..904b51e96 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/InheritanceOrigin.java @@ -37,8 +37,8 @@ public class InheritanceOrigin implements InheritanceOriginMetadata { private final DataType dataType; public InheritanceOrigin(PermissionHolderIdentifier origin, DataType dataType) { - this.origin = origin; - this.dataType = dataType; + this.origin = Objects.requireNonNull(origin, "origin"); + this.dataType = Objects.requireNonNull(dataType, "dataType"); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index fd9e71d4c..d2ab7171a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -110,7 +110,7 @@ public abstract class PermissionHolder { * * @see #normalData() */ - private final RecordedNodeMap normalNodes = new RecordedNodeMap(new NodeMapMutable(this, DataType.NORMAL)); + private final RecordedNodeMap normalNodes; /** * The holders transient nodes. @@ -122,12 +122,12 @@ public abstract class PermissionHolder { * * @see #transientData() */ - private final NodeMap transientNodes = new NodeMapMutable(this, DataType.TRANSIENT); + private final NodeMap transientNodes; /** * Comparator used to ordering groups when calculating inheritance */ - private final Comparator inheritanceComparator = InheritanceComparator.getFor(this); + private final Comparator inheritanceComparator; /** * Creates a new instance @@ -137,6 +137,9 @@ public abstract class PermissionHolder { protected PermissionHolder(LuckPermsPlugin plugin, String objectName) { this.plugin = plugin; this.identifier = new PermissionHolderIdentifier(getType(), objectName); + this.normalNodes = new RecordedNodeMap(new NodeMapMutable(this, DataType.NORMAL)); + this.transientNodes = new NodeMapMutable(this, DataType.TRANSIENT); + this.inheritanceComparator = InheritanceComparator.getFor(this); } // getters diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java index dc829ff69..4d5c772e1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolderIdentifier.java @@ -36,15 +36,15 @@ public final class PermissionHolderIdentifier implements Identifier { private final String name; public PermissionHolderIdentifier(HolderType type, String name) { - this.type = type == HolderType.USER + this.type = Objects.requireNonNull(type, "type") == HolderType.USER ? Identifier.USER_TYPE : Identifier.GROUP_TYPE; - this.name = name; + this.name = Objects.requireNonNull(name, "name"); } public PermissionHolderIdentifier(String type, String name) { - this.type = type; - this.name = name; + this.type = Objects.requireNonNull(type, "type"); + this.name = Objects.requireNonNull(name, "name"); } @Override From d3029a8467ddee25ba274118905fabeb0b78e97c Mon Sep 17 00:00:00 2001 From: lucko Date: Mon, 7 Feb 2022 19:13:09 +0000 Subject: [PATCH 060/314] Web editor socket connection (#3303) --- bukkit/src/main/resources/luckperms.commodore | 3 + .../common/command/CommandManager.java | 2 + .../command/access/CommandPermission.java | 1 + .../common/command/spec/CommandSpec.java | 8 +- .../commands/generic/other/HolderEditor.java | 4 +- .../commands/misc/ApplyEditsCommand.java | 2 +- .../common/commands/misc/EditorCommand.java | 4 +- .../commands/misc/TrustEditorCommand.java | 69 +++++ .../common/commands/track/TrackEditor.java | 4 +- .../luckperms/common/config/ConfigKeys.java | 7 +- .../common/event/EventDispatcher.java | 12 +- .../luckperms/common/http/BytebinClient.java | 14 +- .../common/http/BytesocksClient.java | 101 +++++++ .../luckperms/common/locale/Message.java | 91 ++++++ .../common/model/PermissionHolder.java | 29 +- .../common/model/nodemap/MutateResult.java | 152 ----------- .../common/model/nodemap/NodeMap.java | 27 +- .../common/model/nodemap/NodeMapMutable.java | 76 ++++-- .../common/model/nodemap/RecordedNodeMap.java | 41 +-- .../plugin/AbstractLuckPermsPlugin.java | 18 +- .../common/plugin/LuckPermsPlugin.java | 16 +- .../implementation/sql/SqlStorage.java | 10 +- .../luckperms/common/util/Difference.java | 208 ++++++++++++++ .../common/webeditor/WebEditorRequest.java | 143 +++++----- .../common/webeditor/WebEditorResponse.java | 141 ++++++---- .../common/webeditor/WebEditorSession.java | 206 ++++++++++++++ .../webeditor/socket/CryptographyUtils.java | 118 ++++++++ .../webeditor/socket/SocketMessageType.java | 78 ++++++ .../webeditor/socket/WebEditorSocket.java | 258 ++++++++++++++++++ .../listener/Handler.java} | 23 +- .../socket/listener/HandlerChangeRequest.java | 105 +++++++ .../socket/listener/HandlerConnected.java | 50 ++++ .../socket/listener/HandlerHello.java | 130 +++++++++ .../socket/listener/HandlerPing.java | 60 ++++ .../listener/WebEditorSocketListener.java | 181 ++++++++++++ .../common/webeditor/store/RemoteSession.java | 51 ++++ .../webeditor/store/WebEditorKeystore.java | 196 +++++++++++++ .../WebEditorSessionMap.java} | 34 +-- .../webeditor/store/WebEditorSocketMap.java | 50 ++++ .../webeditor/store/WebEditorStore.java | 57 ++++ .../main/resources/luckperms_en.properties | 12 + .../listeners/FabricCommandListUpdater.java | 6 +- .../fabric/mixin/CommandManagerMixin.java | 1 + 43 files changed, 2381 insertions(+), 418 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/commands/misc/TrustEditorCommand.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/http/BytesocksClient.java delete mode 100644 common/src/main/java/me/lucko/luckperms/common/model/nodemap/MutateResult.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/util/Difference.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/socket/CryptographyUtils.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/socket/SocketMessageType.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java rename common/src/main/java/me/lucko/luckperms/common/webeditor/{SessionState.java => socket/listener/Handler.java} (75%) create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerChangeRequest.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerConnected.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerHello.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerPing.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/WebEditorSocketListener.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/store/RemoteSession.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorKeystore.java rename common/src/main/java/me/lucko/luckperms/common/webeditor/{WebEditorSessionStore.java => store/WebEditorSessionMap.java} (64%) create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSocketMap.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java diff --git a/bukkit/src/main/resources/luckperms.commodore b/bukkit/src/main/resources/luckperms.commodore index 5d043c1ca..9d9455bba 100644 --- a/bukkit/src/main/resources/luckperms.commodore +++ b/bukkit/src/main/resources/luckperms.commodore @@ -57,6 +57,9 @@ luckperms { applyedits { code brigadier:string single_word; } + trusteditor { + id brigadier:string single_word; + } creategroup { name brigadier:string single_word { weight brigadier:integer { diff --git a/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java b/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java index e9d206227..3443eff42 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java @@ -51,6 +51,7 @@ import me.lucko.luckperms.common.commands.misc.SyncCommand; import me.lucko.luckperms.common.commands.misc.TranslationsCommand; import me.lucko.luckperms.common.commands.misc.TreeCommand; +import me.lucko.luckperms.common.commands.misc.TrustEditorCommand; import me.lucko.luckperms.common.commands.misc.VerboseCommand; import me.lucko.luckperms.common.commands.track.CreateTrack; import me.lucko.luckperms.common.commands.track.DeleteTrack; @@ -123,6 +124,7 @@ public CommandManager(LuckPermsPlugin plugin) { .add(new BulkUpdateCommand()) .add(new TranslationsCommand()) .add(new ApplyEditsCommand()) + .add(new TrustEditorCommand()) .add(new CreateGroup()) .add(new DeleteGroup()) .add(new ListGroups()) diff --git a/common/src/main/java/me/lucko/luckperms/common/command/access/CommandPermission.java b/common/src/main/java/me/lucko/luckperms/common/command/access/CommandPermission.java index 606c4b790..dfb5efc3a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/access/CommandPermission.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/access/CommandPermission.java @@ -44,6 +44,7 @@ public enum CommandPermission { RELOAD_CONFIG("reloadconfig", Type.NONE), BULK_UPDATE("bulkupdate", Type.NONE), APPLY_EDITS("applyedits", Type.NONE), + TRUST_EDITOR("trusteditor", Type.NONE), TRANSLATIONS("translations", Type.NONE), CREATE_GROUP("creategroup", Type.NONE), diff --git a/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java b/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java index c162d1b44..f749a3117 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/spec/CommandSpec.java @@ -86,9 +86,11 @@ public enum CommandSpec { TRANSLATIONS("/%s translations", arg("install", false) ), - APPLY_EDITS("/%s applyedits [target]", - arg("code", true), - arg("target", false) + APPLY_EDITS("/%s applyedits ", + arg("code", true) + ), + TRUST_EDITOR("/%s trusteditor ", + arg("id", true) ), CREATE_GROUP("/%s creategroup ", diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java index 0dfaa0da2..d7bcaf5bd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java @@ -42,6 +42,7 @@ import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.common.webeditor.WebEditorRequest; +import me.lucko.luckperms.common.webeditor.WebEditorSession; import net.luckperms.api.node.Node; @@ -84,8 +85,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, T target, ArgumentLis Message.EDITOR_START.send(sender); - WebEditorRequest.generate(holders, Collections.emptyList(), sender, label, plugin) - .createSession(plugin, sender); + WebEditorSession.createAndOpen(holders, Collections.emptyList(), sender, label, plugin); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java index 90c584d03..f5ca93e40 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java @@ -73,7 +73,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St return; } - new WebEditorResponse(code, data).apply(plugin, sender, label, ignoreSessionWarning); + new WebEditorResponse(code, data).apply(plugin, sender, null, label, ignoreSessionWarning); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java index d96eba560..46ac1698d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java @@ -40,6 +40,7 @@ import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.common.webeditor.WebEditorRequest; +import me.lucko.luckperms.common.webeditor.WebEditorSession; import net.luckperms.api.node.Node; @@ -107,8 +108,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, St Message.EDITOR_START.send(sender); - WebEditorRequest.generate(holders, tracks, sender, label, plugin) - .createSession(plugin, sender); + WebEditorSession.createAndOpen(holders, tracks, sender, label, plugin); } private enum Type { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/TrustEditorCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/TrustEditorCommand.java new file mode 100644 index 000000000..417a0d589 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/TrustEditorCommand.java @@ -0,0 +1,69 @@ +/* + * 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.commands.misc; + +import me.lucko.luckperms.common.command.abstraction.SingleCommand; +import me.lucko.luckperms.common.command.access.CommandPermission; +import me.lucko.luckperms.common.command.spec.CommandSpec; +import me.lucko.luckperms.common.command.utils.ArgumentList; +import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.Predicates; +import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; + +public class TrustEditorCommand extends SingleCommand { + public TrustEditorCommand() { + super(CommandSpec.TRUST_EDITOR, "TrustEditor", CommandPermission.TRUST_EDITOR, Predicates.not(1)); + } + + @Override + public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, String label) { + String id = args.get(0); + + if (id.isEmpty()) { + Message.APPLY_EDITS_INVALID_CODE.send(sender, id); + return; + } + + WebEditorSocket socket = plugin.getWebEditorStore().sockets().getSocket(sender); + if (socket == null) { + Message.EDITOR_SOCKET_TRUST_FAILURE.send(sender); + return; + } + + if (socket.trustConnection(id)) { + Message.EDITOR_SOCKET_TRUST_SUCCESS.send(sender); + } else { + Message.EDITOR_SOCKET_TRUST_FAILURE.send(sender); + } + } + + @Override + public boolean shouldDisplay() { + return false; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java index 50af93903..215531e62 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/track/TrackEditor.java @@ -42,6 +42,7 @@ import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.common.webeditor.WebEditorRequest; +import me.lucko.luckperms.common.webeditor.WebEditorSession; import net.luckperms.api.node.Node; @@ -97,8 +98,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Track target, Argumen Message.EDITOR_START.send(sender); - WebEditorRequest.generate(holders, Collections.singletonList(target), sender, label, plugin) - .createSession(plugin, sender); + WebEditorSession.createAndOpen(holders, Collections.singletonList(target), sender, label, plugin); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 970ca9760..38a228a41 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -692,7 +692,12 @@ private ConfigKeys() {} /** * The URL of the bytebin instance used to upload data */ - public static final ConfigKey BYTEBIN_URL = stringKey("bytebin-url", "https://bytebin.lucko.me/"); + public static final ConfigKey BYTEBIN_URL = stringKey("bytebin-url", "https://usercontent.luckperms.net/"); + + /** + * The host of the bytesocks instance used to communicate with + */ + public static final ConfigKey BYTESOCKS_HOST = stringKey("bytesocks-host", "usersockets.luckperms.net"); /** * The URL of the web editor 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 b56989967..db5a774f1 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 @@ -40,8 +40,8 @@ import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.User; -import me.lucko.luckperms.common.model.nodemap.MutateResult; import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.Difference; import net.luckperms.api.actionlog.Action; import net.luckperms.api.event.LuckPermsEvent; @@ -226,7 +226,7 @@ public void dispatchLogReceive(UUID id, Action entry) { postAsync(LogReceiveEvent.class, id, entry); } - public void dispatchNodeChanges(PermissionHolder target, DataType dataType, MutateResult changes) { + public void dispatchNodeChanges(PermissionHolder target, DataType dataType, Difference changes) { if (!this.eventBus.shouldPost(NodeAddEvent.class) && !this.eventBus.shouldPost(NodeRemoveEvent.class)) { return; } @@ -239,15 +239,15 @@ public void dispatchNodeChanges(PermissionHolder target, DataType dataType, Muta ImmutableSet state = target.getData(dataType).asImmutableSet(); // call an event for each recorded change - for (MutateResult.Change change : changes.getChanges()) { - Class type = change.getType() == MutateResult.ChangeType.ADD ? + for (Difference.Change change : changes.getChanges()) { + Class type = change.type() == Difference.ChangeType.ADD ? NodeAddEvent.class : NodeRemoveEvent.class; - postAsync(type, proxy, dataType, state, change.getNode()); + postAsync(type, proxy, dataType, state, change.value()); } } - public void dispatchNodeClear(PermissionHolder target, DataType dataType, MutateResult changes) { + public void dispatchNodeClear(PermissionHolder target, DataType dataType, Difference changes) { if (!this.eventBus.shouldPost(NodeClearEvent.class)) { return; } diff --git a/common/src/main/java/me/lucko/luckperms/common/http/BytebinClient.java b/common/src/main/java/me/lucko/luckperms/common/http/BytebinClient.java index 94326efd4..ad50d7d70 100644 --- a/common/src/main/java/me/lucko/luckperms/common/http/BytebinClient.java +++ b/common/src/main/java/me/lucko/luckperms/common/http/BytebinClient.java @@ -83,15 +83,21 @@ public Response makeHttpRequest(Request request) throws IOException, Unsuccessfu * * @param buf the compressed content * @param contentType the type of the content + * @param userAgentExtra extra string to append to the user agent * @return the key of the resultant content * @throws IOException if an error occurs */ - public Content postContent(byte[] buf, MediaType contentType) throws IOException, UnsuccessfulRequestException { + public Content postContent(byte[] buf, MediaType contentType, String userAgentExtra) throws IOException, UnsuccessfulRequestException { RequestBody body = RequestBody.create(contentType, buf); + String userAgent = this.userAgent; + if (userAgentExtra != null) { + userAgent += "/" + userAgentExtra; + } + Request.Builder requestBuilder = new Request.Builder() .url(this.url + "post") - .header("User-Agent", this.userAgent) + .header("User-Agent", userAgent) .header("Content-Encoding", "gzip"); Request request = requestBuilder.post(body).build(); @@ -104,6 +110,10 @@ public Content postContent(byte[] buf, MediaType contentType) throws IOException } } + public Content postContent(byte[] buf, MediaType contentType) throws IOException, UnsuccessfulRequestException { + return postContent(buf, contentType, null); + } + /** * GETs json content from bytebin * diff --git a/common/src/main/java/me/lucko/luckperms/common/http/BytesocksClient.java b/common/src/main/java/me/lucko/luckperms/common/http/BytesocksClient.java new file mode 100644 index 000000000..61affe512 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/http/BytesocksClient.java @@ -0,0 +1,101 @@ +/* + * 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.http; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; + +import java.io.IOException; +import java.util.Objects; + +public class BytesocksClient extends AbstractHttpClient { + + /* The bytesocks urls */ + private final String httpUrl; + private final String wsUrl; + + /** The client user agent */ + private final String userAgent; + + /** + * Creates a new bytesocks instance + * + * @param host the bytesocks host + * @param userAgent the client user agent string + */ + public BytesocksClient(OkHttpClient okHttpClient, String host, String userAgent) { + super(okHttpClient); + + this.httpUrl = "https://" + host + "/"; + this.wsUrl = "wss://" + host + "/"; + this.userAgent = userAgent; + } + + public Socket createSocket(WebSocketListener listener) throws IOException, UnsuccessfulRequestException { + Request createRequest = new Request.Builder() + .url(this.httpUrl + "create") + .header("User-Agent", this.userAgent) + .build(); + + String id; + try (Response response = makeHttpRequest(createRequest)) { + if (response.code() != 201) { + throw new UnsuccessfulRequestException(response); + } + + id = Objects.requireNonNull(response.header("Location")); + } + + Request socketRequest = new Request.Builder() + .url(this.wsUrl + id) + .header("User-Agent", this.userAgent) + .build(); + + return new Socket(id, this.okHttp.newWebSocket(socketRequest, listener)); + } + + public static final class Socket { + private final String channelId; + private final WebSocket socket; + + public Socket(String channelId, WebSocket socket) { + this.channelId = channelId; + this.socket = socket; + } + + public String channelId() { + return this.channelId; + } + + public WebSocket socket() { + return this.socket; + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index 5f340c5a2..0be4e97e1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -1159,6 +1159,97 @@ static TextComponent prefixed(ComponentLike component) { .clickEvent(ClickEvent.openUrl(url)) ); + Args0 EDITOR_SOCKET_CONNECTED = () -> prefixed(translatable() + // "&bEditor window connected successfully." + .color(AQUA) + .key("luckperms.command.editor.socket.connected") + .append(FULL_STOP) + ); + + Args0 EDITOR_SOCKET_RECONNECTED = () -> prefixed(translatable() + // "&7Editor window reconnected successfully." + .color(GRAY) + .key("luckperms.command.editor.socket.reconnected") + .append(FULL_STOP) + ); + + Args0 EDITOR_SOCKET_CHANGES_RECEIVED = () -> prefixed(translatable() + // "&7Changes have been received from the connected web editor session." + .color(GRAY) + .key("luckperms.command.editor.socket.changes-received") + .append(FULL_STOP) + ); + + Args4 EDITOR_SOCKET_UNTRUSTED = (nonce, browser, cmdLabel, console) -> join(newline(), + // "&bAn editor window has connected, but it is not yet trusted." + // "&8(&7session id = &faaaaa&7, browser = &fChrome on Windows 10&8)" + // "&7If it was you, &aclick here&7 to trust the session!" + // "&7If it was you, run &a/lp trusteditor aaaaa&7 to trust the session!" + prefixed(translatable() + .key("luckperms.command.editor.socket.untrusted") + .color(AQUA) + .append(FULL_STOP)), + prefixed(text() + .color(DARK_GRAY) + .append(OPEN_BRACKET) + .append(translatable() + .key("luckperms.command.editor.socket.untrusted.sessioninfo") + .color(GRAY) + .args( + text(nonce, WHITE), + text(browser, WHITE) + ) + ) + .append(CLOSE_BRACKET) + ), + prefixed(text() + .color(GRAY) + .apply(builder -> { + String command = "/" + cmdLabel + " trusteditor " + nonce; + if (console) { + builder.append(translatable() + .key("luckperms.command.editor.socket.untrusted.prompt.runcommand") + .args(text(command, GREEN)) + .build() + ); + } else { + builder.append(translatable() + .key("luckperms.command.editor.socket.untrusted.prompt.click") + .args(translatable() + .key("luckperms.command.editor.socket.untrusted.prompt.click.action") + .color(GREEN) + .clickEvent(ClickEvent.runCommand(command)) + ) + .build() + ); + } + })) + ); + + Args0 EDITOR_SOCKET_TRUST_SUCCESS = () -> join(newline(), + // "&aThe editor session has been marked as trusted." + // "&7In the future, connections from the same browser will be trusted automatically." + // "&7The plugin will now attempt to establish a connection with the editor..." + prefixed(translatable() + .key("luckperms.command.editor.socket.trust.success") + .color(GREEN) + .append(FULL_STOP)), + prefixed(translatable() + .key("luckperms.command.editor.socket.trust.futureinfo") + .color(GRAY) + .append(FULL_STOP)), + prefixed(translatable() + .key("luckperms.command.editor.socket.trust.connecting") + .color(GRAY)) + ); + + Args0 EDITOR_SOCKET_TRUST_FAILURE = () -> prefixed(translatable() + // "&cUnable to trust the given session because the socket is closed, or because a different connection was established instead." + .color(RED) + .key("luckperms.command.editor.socket.trust.failure") + .append(FULL_STOP) + ); + Args2 EDITOR_HTTP_REQUEST_FAILURE = (code, message) -> prefixed(text() // "&cUnable to communicate with the editor. (response code &4{}&c, message='{}')" .color(RED) diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index d2ab7171a..a4ebf6d7f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -31,7 +31,6 @@ import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator; import me.lucko.luckperms.common.inheritance.InheritanceComparator; import me.lucko.luckperms.common.inheritance.InheritanceGraph; -import me.lucko.luckperms.common.model.nodemap.MutateResult; import me.lucko.luckperms.common.model.nodemap.NodeMap; import me.lucko.luckperms.common.model.nodemap.NodeMapMutable; import me.lucko.luckperms.common.model.nodemap.RecordedNodeMap; @@ -39,6 +38,7 @@ import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.query.DataSelector; +import me.lucko.luckperms.common.util.Difference; import net.kyori.adventure.text.Component; import net.luckperms.api.context.ContextSet; @@ -223,8 +223,17 @@ public void loadNodesFromStorage(Iterable set) { invalidateCache(); } - public MutateResult setNodes(DataType type, Iterable set, boolean callEvent) { - MutateResult res = getData(type).setContent(set); + public Difference setNodes(DataType type, Iterable set, boolean callEvent) { + Difference res = getData(type).setContent(set); + invalidateCache(); + if (callEvent) { + getPlugin().getEventDispatcher().dispatchNodeChanges(this, type, res); + } + return res; + } + + public Difference setNodes(DataType type, Difference changes, boolean callEvent) { + Difference res = getData(type).applyChanges(changes); invalidateCache(); if (callEvent) { getPlugin().getEventDispatcher().dispatchNodeChanges(this, type, res); @@ -420,7 +429,7 @@ public boolean auditTemporaryNodes() { } private boolean auditTemporaryNodes(DataType dataType) { - MutateResult result = getData(dataType).removeIf(Node::hasExpired); + Difference result = getData(dataType).removeIf(Node::hasExpired); if (!result.isEmpty()) { invalidateCache(); } @@ -454,7 +463,7 @@ public DataMutateResult setNode(DataType dataType, Node node, boolean callEvent) return DataMutateResult.FAIL_ALREADY_HAS; } - MutateResult changes = getData(dataType).add(node); + Difference changes = getData(dataType).add(node); invalidateCache(); if (callEvent) { this.plugin.getEventDispatcher().dispatchNodeChanges(this, dataType, changes); @@ -491,7 +500,7 @@ public DataMutateResult.WithMergedNode setNode(DataType dataType, Node node, Tem if (newNode != null) { // Remove the old Node & add the new one. - MutateResult changes = data.removeThenAdd(otherMatch, newNode); + Difference changes = data.removeThenAdd(otherMatch, newNode); invalidateCache(); this.plugin.getEventDispatcher().dispatchNodeChanges(this, dataType, changes); @@ -509,7 +518,7 @@ public DataMutateResult unsetNode(DataType dataType, Node node) { return DataMutateResult.FAIL_LACKS; } - MutateResult changes = getData(dataType).remove(node); + Difference changes = getData(dataType).remove(node); invalidateCache(); this.plugin.getEventDispatcher().dispatchNodeChanges(this, dataType, changes); @@ -531,7 +540,7 @@ public DataMutateResult.WithMergedNode unsetNode(DataType dataType, Node node, @ Node newNode = node.toBuilder().expiry(newExpiry).build(); // Remove the old Node & add the new one. - MutateResult changes = data.removeThenAdd(otherMatch, newNode); + Difference changes = data.removeThenAdd(otherMatch, newNode); invalidateCache(); this.plugin.getEventDispatcher().dispatchNodeChanges(this, dataType, changes); @@ -545,7 +554,7 @@ public DataMutateResult.WithMergedNode unsetNode(DataType dataType, Node node, @ } public boolean removeIf(DataType dataType, @Nullable ContextSet contextSet, Predicate predicate, boolean giveDefault) { - MutateResult changes; + Difference changes; if (contextSet == null) { changes = getData(dataType).removeIf(predicate); } else { @@ -566,7 +575,7 @@ public boolean removeIf(DataType dataType, @Nullable ContextSet contextSet, Pred } public boolean clearNodes(DataType dataType, ContextSet contextSet, boolean giveDefault) { - MutateResult changes; + Difference changes; if (contextSet == null) { changes = getData(dataType).clear(); } else { diff --git a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/MutateResult.java b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/MutateResult.java deleted file mode 100644 index aeb554c1f..000000000 --- a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/MutateResult.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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.model.nodemap; - -import net.luckperms.api.node.Node; - -import java.util.LinkedHashSet; -import java.util.Objects; -import java.util.Set; - -/** - * Records a log of the changes that occur as a result of a {@link NodeMap} mutation(s). - */ -public class MutateResult { - private final LinkedHashSet changes = new LinkedHashSet<>(); - - public Set getChanges() { - return this.changes; - } - - public Set getChanges(ChangeType type) { - Set changes = new LinkedHashSet<>(this.changes.size()); - for (Change change : this.changes) { - if (change.getType() == type) { - changes.add(change.getNode()); - } - } - return changes; - } - - void clear() { - this.changes.clear(); - } - - public boolean isEmpty() { - return this.changes.isEmpty(); - } - - public Set getAdded() { - return getChanges(ChangeType.ADD); - } - - public Set getRemoved() { - return getChanges(ChangeType.REMOVE); - } - - private void recordChange(Change change) { - // This method is the magic of this class. - // When tracking, we want to ignore changes that cancel each other out, and only - // keep track of the net difference. - // e.g. adding then removing the same node = zero net change, so ignore it. - - if (this.changes.remove(change.inverse())) { - return; - } - this.changes.add(change); - } - - public void recordChange(ChangeType type, Node node) { - recordChange(new Change(type, node)); - } - - public void recordChanges(ChangeType type, Iterable nodes) { - for (Node node : nodes) { - recordChange(new Change(type, node)); - } - } - - public MutateResult mergeFrom(MutateResult other) { - for (Change change : other.changes) { - recordChange(change); - } - return this; - } - - @Override - public String toString() { - return "MutateResult{changes=" + this.changes + '}'; - } - - public static final class Change { - private final ChangeType type; - private final Node node; - - public Change(ChangeType type, Node node) { - this.type = type; - this.node = node; - } - - public ChangeType getType() { - return this.type; - } - - public Node getNode() { - return this.node; - } - - public Change inverse() { - return new Change(this.type.inverse(), this.node); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Change change = (Change) o; - return this.type == change.type && this.node.equals(change.node); - } - - @Override - public int hashCode() { - return Objects.hash(this.type, this.node); - } - - @Override - public String toString() { - return "Change{type=" + this.type + ", node=" + this.node + '}'; - } - } - - public enum ChangeType { - ADD, REMOVE; - - public ChangeType inverse() { - return this == ADD ? REMOVE : ADD; - } - } - -} diff --git a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMap.java b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMap.java index 117045cac..8e4a0b9ab 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMap.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMap.java @@ -30,6 +30,7 @@ import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator; +import me.lucko.luckperms.common.util.Difference; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.ImmutableContextSet; @@ -134,28 +135,30 @@ default ImmutableSet inheritanceAsImmutableSet() { // mutate methods - MutateResult add(Node nodeWithoutInheritanceOrigin); + Difference add(Node nodeWithoutInheritanceOrigin); - MutateResult remove(Node node); + Difference remove(Node node); - MutateResult removeExact(Node node); + Difference removeExact(Node node); - MutateResult removeIf(Predicate predicate); + Difference removeIf(Predicate predicate); - MutateResult removeIf(ContextSet contextSet, Predicate predicate); + Difference removeIf(ContextSet contextSet, Predicate predicate); - MutateResult removeThenAdd(Node nodeToRemove, Node nodeToAdd); + Difference removeThenAdd(Node nodeToRemove, Node nodeToAdd); - MutateResult clear(); + Difference clear(); - MutateResult clear(ContextSet contextSet); + Difference clear(ContextSet contextSet); - MutateResult setContent(Iterable set); + Difference setContent(Iterable set); - MutateResult setContent(Stream stream); + Difference setContent(Stream stream); - MutateResult addAll(Iterable set); + Difference applyChanges(Difference changes); - MutateResult addAll(Stream stream); + Difference addAll(Iterable set); + + Difference addAll(Stream stream); } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java index 97db73846..291bb9a32 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/NodeMapMutable.java @@ -29,8 +29,9 @@ import me.lucko.luckperms.common.context.comparator.ContextSetComparator; import me.lucko.luckperms.common.model.InheritanceOrigin; import me.lucko.luckperms.common.model.PermissionHolder; -import me.lucko.luckperms.common.model.nodemap.MutateResult.ChangeType; import me.lucko.luckperms.common.node.comparator.NodeComparator; +import me.lucko.luckperms.common.util.Difference; +import me.lucko.luckperms.common.util.Difference.ChangeType; import net.luckperms.api.context.ContextSatisfyMode; import net.luckperms.api.context.ContextSet; @@ -122,11 +123,11 @@ private Node addInheritanceOrigin(Node node) { } @Override - public MutateResult add(Node nodeWithoutInheritanceOrigin) { + public Difference add(Node nodeWithoutInheritanceOrigin) { Node node = addInheritanceOrigin(nodeWithoutInheritanceOrigin); ImmutableContextSet context = node.getContexts(); - MutateResult result = new MutateResult(); + Difference result = new Difference<>(); this.lock.lock(); try { @@ -162,9 +163,9 @@ public MutateResult add(Node nodeWithoutInheritanceOrigin) { } @Override - public MutateResult remove(Node node) { + public Difference remove(Node node) { ImmutableContextSet context = node.getContexts(); - MutateResult result = new MutateResult(); + Difference result = new Difference<>(); this.lock.lock(); try { @@ -191,7 +192,7 @@ public MutateResult remove(Node node) { return result; } - private static void removeMatching(Iterator it, Node node, MutateResult result) { + private static void removeMatching(Iterator it, Node node, Difference result) { while (it.hasNext()) { Node el = it.next(); if (node.equals(el, NodeEqualityPredicate.IGNORE_EXPIRY_TIME_AND_VALUE)) { @@ -201,7 +202,7 @@ private static void removeMatching(Iterator it, Node node, MutateResult re } } - private static void removeMatchingButNotSame(Iterator it, Node node, MutateResult result) { + private static void removeMatchingButNotSame(Iterator it, Node node, Difference result) { while (it.hasNext()) { Node el = it.next(); if (el != node && node.equals(el, NodeEqualityPredicate.IGNORE_EXPIRY_TIME_AND_VALUE)) { @@ -212,9 +213,9 @@ private static void removeMatchingButNotSame(Iterator it, Node node, Mutat } @Override - public MutateResult removeExact(Node node) { + public Difference removeExact(Node node) { ImmutableContextSet context = node.getContexts(); - MutateResult result = new MutateResult(); + Difference result = new Difference<>(); this.lock.lock(); try { @@ -245,8 +246,8 @@ public MutateResult removeExact(Node node) { } @Override - public MutateResult removeIf(Predicate predicate) { - MutateResult result = new MutateResult(); + public Difference removeIf(Predicate predicate) { + Difference result = new Difference<>(); this.lock.lock(); try { @@ -261,9 +262,9 @@ public MutateResult removeIf(Predicate predicate) { } @Override - public MutateResult removeIf(ContextSet contextSet, Predicate predicate) { + public Difference removeIf(ContextSet contextSet, Predicate predicate) { ImmutableContextSet context = contextSet.immutableCopy(); - MutateResult result = new MutateResult(); + Difference result = new Difference<>(); this.lock.lock(); try { @@ -279,7 +280,7 @@ public MutateResult removeIf(ContextSet contextSet, Predicate pred return result; } - private void removeMatching(Iterator it, Predicate predicate, MutateResult result) { + private void removeMatching(Iterator it, Predicate predicate, Difference result) { while (it.hasNext()) { Node node = it.next(); @@ -300,9 +301,9 @@ private void removeMatching(Iterator it, Predicate predicate } @Override - public MutateResult removeThenAdd(Node nodeToRemove, Node nodeToAdd) { + public Difference removeThenAdd(Node nodeToRemove, Node nodeToAdd) { if (nodeToAdd.equals(nodeToRemove)) { - return new MutateResult(); + return new Difference<>(); } this.lock.lock(); @@ -314,8 +315,8 @@ public MutateResult removeThenAdd(Node nodeToRemove, Node nodeToAdd) { } @Override - public MutateResult clear() { - MutateResult result = new MutateResult(); + public Difference clear() { + Difference result = new Difference<>(); this.lock.lock(); try { @@ -336,9 +337,9 @@ public MutateResult clear() { } @Override - public MutateResult clear(ContextSet contextSet) { + public Difference clear(ContextSet contextSet) { ImmutableContextSet context = contextSet.immutableCopy(); - MutateResult result = new MutateResult(); + Difference result = new Difference<>(); this.lock.lock(); try { @@ -355,8 +356,8 @@ public MutateResult clear(ContextSet contextSet) { } @Override - public MutateResult setContent(Iterable set) { - MutateResult result = new MutateResult(); + public Difference setContent(Iterable set) { + Difference result = new Difference<>(); this.lock.lock(); try { @@ -370,8 +371,8 @@ public MutateResult setContent(Iterable set) { } @Override - public MutateResult setContent(Stream stream) { - MutateResult result = new MutateResult(); + public Difference setContent(Stream stream) { + Difference result = new Difference<>(); this.lock.lock(); try { @@ -385,8 +386,27 @@ public MutateResult setContent(Stream stream) { } @Override - public MutateResult addAll(Iterable set) { - MutateResult result = new MutateResult(); + public Difference applyChanges(Difference changes) { + Difference result = new Difference<>(); + + this.lock.lock(); + try { + for (Node n : changes.getAdded()) { + result.mergeFrom(add(n)); + } + for (Node n : changes.getRemoved()) { + result.mergeFrom(removeExact(n)); + } + } finally { + this.lock.unlock(); + } + + return result; + } + + @Override + public Difference addAll(Iterable set) { + Difference result = new Difference<>(); this.lock.lock(); try { @@ -401,8 +421,8 @@ public MutateResult addAll(Iterable set) { } @Override - public MutateResult addAll(Stream stream) { - MutateResult result = new MutateResult(); + public Difference addAll(Stream stream) { + Difference result = new Difference<>(); this.lock.lock(); try { diff --git a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/RecordedNodeMap.java b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/RecordedNodeMap.java index 10d24cad9..37eebf274 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/nodemap/RecordedNodeMap.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/nodemap/RecordedNodeMap.java @@ -28,6 +28,8 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableSet; +import me.lucko.luckperms.common.util.Difference; + import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.node.Node; @@ -53,7 +55,7 @@ public class RecordedNodeMap implements NodeMap { private final NodeMap delegate; private final Lock lock = new ReentrantLock(); - private MutateResult changes = new MutateResult(); + private Difference changes = new Difference<>(); public RecordedNodeMap(NodeMap delegate) { this.delegate = delegate; @@ -72,12 +74,12 @@ public void discardChanges() { } } - public MutateResult exportChanges(Predicate onlyIf) { + public Difference exportChanges(Predicate> onlyIf) { this.lock.lock(); try { - MutateResult existing = this.changes; + Difference existing = this.changes; if (onlyIf.test(existing)) { - this.changes = new MutateResult(); + this.changes = new Difference<>(); return existing; } return null; @@ -86,7 +88,7 @@ public MutateResult exportChanges(Predicate onlyIf) { } } - private MutateResult record(MutateResult result) { + private Difference record(Difference result) { this.lock.lock(); try { this.changes.mergeFrom(result); @@ -99,62 +101,67 @@ private MutateResult record(MutateResult result) { // delegate, but pass the result through #record(MutateResult) @Override - public MutateResult add(Node nodeWithoutInheritanceOrigin) { + public Difference add(Node nodeWithoutInheritanceOrigin) { return record(this.delegate.add(nodeWithoutInheritanceOrigin)); } @Override - public MutateResult remove(Node node) { + public Difference remove(Node node) { return record(this.delegate.remove(node)); } @Override - public MutateResult removeExact(Node node) { + public Difference removeExact(Node node) { return record(this.delegate.removeExact(node)); } @Override - public MutateResult removeIf(Predicate predicate) { + public Difference removeIf(Predicate predicate) { return record(this.delegate.removeIf(predicate)); } @Override - public MutateResult removeIf(ContextSet contextSet, Predicate predicate) { + public Difference removeIf(ContextSet contextSet, Predicate predicate) { return record(this.delegate.removeIf(contextSet, predicate)); } @Override - public MutateResult removeThenAdd(Node nodeToRemove, Node nodeToAdd) { + public Difference removeThenAdd(Node nodeToRemove, Node nodeToAdd) { return record(this.delegate.removeThenAdd(nodeToRemove, nodeToAdd)); } @Override - public MutateResult clear() { + public Difference clear() { return record(this.delegate.clear()); } @Override - public MutateResult clear(ContextSet contextSet) { + public Difference clear(ContextSet contextSet) { return record(this.delegate.clear(contextSet)); } @Override - public MutateResult setContent(Iterable set) { + public Difference setContent(Iterable set) { return record(this.delegate.setContent(set)); } @Override - public MutateResult setContent(Stream stream) { + public Difference setContent(Stream stream) { return record(this.delegate.setContent(stream)); } @Override - public MutateResult addAll(Iterable set) { + public Difference applyChanges(Difference changes) { + return record(this.delegate.applyChanges(changes)); + } + + @Override + public Difference addAll(Iterable set) { return record(this.delegate.addAll(set)); } @Override - public MutateResult addAll(Stream stream) { + public Difference addAll(Stream stream) { return record(this.delegate.addAll(stream)); } diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 871fefa99..4836b9e76 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -40,6 +40,7 @@ import me.lucko.luckperms.common.event.gen.GeneratedEventClass; import me.lucko.luckperms.common.extension.SimpleExtensionManager; import me.lucko.luckperms.common.http.BytebinClient; +import me.lucko.luckperms.common.http.BytesocksClient; import me.lucko.luckperms.common.inheritance.InheritanceGraphFactory; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.locale.TranslationManager; @@ -57,7 +58,7 @@ import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; -import me.lucko.luckperms.common.webeditor.WebEditorSessionStore; +import me.lucko.luckperms.common.webeditor.store.WebEditorStore; import net.luckperms.api.LuckPerms; @@ -90,7 +91,8 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { private LogDispatcher logDispatcher; private LuckPermsConfiguration configuration; private BytebinClient bytebin; - private WebEditorSessionStore webEditorSessionStore; + private BytesocksClient bytesocks; + private WebEditorStore webEditorStore; private TranslationRepository translationRepository; private FileWatcher fileWatcher = null; private Storage storage; @@ -136,7 +138,8 @@ public final void enable() { .build(); this.bytebin = new BytebinClient(httpClient, getConfiguration().get(ConfigKeys.BYTEBIN_URL), "luckperms"); - this.webEditorSessionStore = new WebEditorSessionStore(); + this.bytesocks = new BytesocksClient(httpClient, getConfiguration().get(ConfigKeys.BYTESOCKS_HOST), "luckperms/editor"); + this.webEditorStore = new WebEditorStore(this); // init translation repo and update bundle files this.translationRepository = new TranslationRepository(this); @@ -420,8 +423,13 @@ public BytebinClient getBytebin() { } @Override - public WebEditorSessionStore getWebEditorSessionStore() { - return this.webEditorSessionStore; + public BytesocksClient getBytesocks() { + return this.bytesocks; + } + + @Override + public WebEditorStore getWebEditorStore() { + return this.webEditorStore; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java index 89e83ada1..e6c5ab0c2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java @@ -36,6 +36,7 @@ import me.lucko.luckperms.common.event.EventDispatcher; import me.lucko.luckperms.common.extension.SimpleExtensionManager; import me.lucko.luckperms.common.http.BytebinClient; +import me.lucko.luckperms.common.http.BytesocksClient; import me.lucko.luckperms.common.inheritance.InheritanceGraphFactory; import me.lucko.luckperms.common.locale.TranslationManager; import me.lucko.luckperms.common.locale.TranslationRepository; @@ -55,7 +56,7 @@ import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; -import me.lucko.luckperms.common.webeditor.WebEditorSessionStore; +import me.lucko.luckperms.common.webeditor.store.WebEditorStore; import net.luckperms.api.query.QueryOptions; @@ -250,11 +251,18 @@ public interface LuckPermsPlugin { BytebinClient getBytebin(); /** - * Gets the web editor session store + * Gets the bytesocks instance in use by platform. * - * @return the web editor session store + * @return the bytesocks instance */ - WebEditorSessionStore getWebEditorSessionStore(); + BytesocksClient getBytesocks(); + + /** + * Gets the web editor store + * + * @return the web editor store + */ + WebEditorStore getWebEditorStore(); /** * Gets a calculated context instance for the user using the rules of the platform. diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index 1e16da190..ee7d653ab 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -39,7 +39,6 @@ import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.manager.group.GroupManager; -import me.lucko.luckperms.common.model.nodemap.MutateResult; import me.lucko.luckperms.common.node.factory.NodeBuilders; import me.lucko.luckperms.common.node.matcher.ConstraintNodeMatcher; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; @@ -47,6 +46,7 @@ import me.lucko.luckperms.common.storage.implementation.sql.connection.ConnectionFactory; import me.lucko.luckperms.common.storage.misc.NodeEntry; import me.lucko.luckperms.common.storage.misc.PlayerSaveResultImpl; +import me.lucko.luckperms.common.util.Difference; import me.lucko.luckperms.common.util.Uuids; import me.lucko.luckperms.common.util.gson.GsonProvider; @@ -351,15 +351,15 @@ public User loadUser(UUID uniqueId, String username) throws SQLException { @Override public void saveUser(User user) throws SQLException { - MutateResult changes = user.normalData().exportChanges(results -> { + Difference changes = user.normalData().exportChanges(results -> { if (this.plugin.getUserManager().isNonDefaultUser(user)) { return true; } // if the only change is adding the default node, we don't need to export if (results.getChanges().size() == 1) { - MutateResult.Change onlyChange = results.getChanges().iterator().next(); - return !(onlyChange.getType() == MutateResult.ChangeType.ADD && this.plugin.getUserManager().isDefaultNode(onlyChange.getNode())); + Difference.Change onlyChange = results.getChanges().iterator().next(); + return !(onlyChange.type() == Difference.ChangeType.ADD && this.plugin.getUserManager().isDefaultNode(onlyChange.value())); } return true; @@ -480,7 +480,7 @@ public void loadAllGroups() throws SQLException { @Override public void saveGroup(Group group) throws SQLException { - MutateResult changes = group.normalData().exportChanges(c -> true); + Difference changes = group.normalData().exportChanges(c -> true); if (!changes.isEmpty()) { try (Connection c = this.connectionFactory.getConnection()) { diff --git a/common/src/main/java/me/lucko/luckperms/common/util/Difference.java b/common/src/main/java/me/lucko/luckperms/common/util/Difference.java new file mode 100644 index 000000000..d0a56fd29 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/util/Difference.java @@ -0,0 +1,208 @@ +/* + * 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.util; + +import java.util.LinkedHashSet; +import java.util.Objects; +import java.util.Set; + +/** + * Records a log of the changes that occur as a result + * of mutations (add or remove operations). + * + * @param the value type + */ +public class Difference { + private final LinkedHashSet> changes = new LinkedHashSet<>(); + + /** + * Gets the recorded changes. + * + * @return the changes + */ + public Set> getChanges() { + return this.changes; + } + + /** + * Gets if no changes have been recorded. + * + * @return if no changes have been recorded + */ + public boolean isEmpty() { + return this.changes.isEmpty(); + } + + /** + * Gets the recorded changes of a given type + * + * @param type the type of change + * @return the changes + */ + public Set getChanges(ChangeType type) { + Set changes = new LinkedHashSet<>(this.changes.size()); + for (Change change : this.changes) { + if (change.type() == type) { + changes.add(change.value()); + } + } + return changes; + } + + /** + * Gets the values that have been added. + * + * @return the added values + */ + public Set getAdded() { + return getChanges(ChangeType.ADD); + } + + /** + * Gets the values that have been removed. + * + * @return the removed values + */ + public Set getRemoved() { + return getChanges(ChangeType.REMOVE); + } + + /** + * Clears all recorded changes. + */ + public void clear() { + this.changes.clear(); + } + + private void recordChange(Change change) { + // This method is the magic of this class. + // When tracking, we want to ignore changes that cancel each other out, and only + // keep track of the net difference. + // e.g. adding then removing the same value = zero net change, so ignore it. + + if (this.changes.remove(change.inverse())) { + return; + } + this.changes.add(change); + } + + /** + * Records a change. + * + * @param type the type of change + * @param value the changed value + */ + public void recordChange(ChangeType type, T value) { + recordChange(new Change<>(type, value)); + } + + /** + * Records some changes. + * + * @param type the type of change + * @param values the changed values + */ + public void recordChanges(ChangeType type, Iterable values) { + for (T value : values) { + recordChange(new Change<>(type, value)); + } + } + + /** + * Merges the recorded differences in {@code other} into this. + * + * @param other the other differences + * @return this + */ + public Difference mergeFrom(Difference other) { + for (Change change : other.changes) { + recordChange(change); + } + return this; + } + + @Override + public String toString() { + return "Difference{" + this.changes + '}'; + } + + /** + * A single change recorded in the {@link Difference} tracker. + * + * @param the value type + */ + public static final class Change { + private final ChangeType type; + private final T value; + + public Change(ChangeType type, T value) { + this.type = type; + this.value = value; + } + + public ChangeType type() { + return this.type; + } + + public T value() { + return this.value; + } + + public Change inverse() { + return new Change<>(this.type.inverse(), this.value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Change change = (Change) o; + return this.type == change.type && this.value.equals(change.value); + } + + @Override + public int hashCode() { + return Objects.hash(this.type, this.value); + } + + @Override + public String toString() { + return "(" + this.type + ": " + this.value + ')'; + } + } + + /** + * The type of change. + */ + public enum ChangeType { + ADD, REMOVE; + + public ChangeType inverse() { + return this == ADD ? REMOVE : ADD; + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java index 0a7ce368f..f3982d2ed 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java @@ -28,14 +28,11 @@ import com.google.common.base.Preconditions; import com.google.gson.JsonObject; -import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.common.context.serializer.ContextSetJsonSerializer; -import me.lucko.luckperms.common.http.AbstractHttpClient; -import me.lucko.luckperms.common.http.UnsuccessfulRequestException; -import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; +import me.lucko.luckperms.common.model.PermissionHolderIdentifier; import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.matcher.ConstraintNodeMatcher; @@ -43,6 +40,7 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.storage.misc.NodeEntry; +import me.lucko.luckperms.common.util.ImmutableCollectors; import me.lucko.luckperms.common.util.gson.GsonProvider; import me.lucko.luckperms.common.util.gson.JArray; import me.lucko.luckperms.common.util.gson.JObject; @@ -64,6 +62,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import java.util.zip.GZIPOutputStream; @@ -75,6 +74,48 @@ public class WebEditorRequest { public static final int MAX_USERS = 500; + /** + * The encoded json object this payload is made up of + */ + private final JsonObject payload; + + private final Map> holders; + private final Map> tracks; + + private WebEditorRequest(JsonObject payload, Map> holders, Map> tracks) { + this.payload = payload; + this.holders = holders.entrySet().stream().collect(ImmutableCollectors.toMap( + e -> e.getKey().getIdentifier(), + Map.Entry::getValue + )); + this.tracks = tracks.entrySet().stream().collect(ImmutableCollectors.toMap( + e -> e.getKey().getName(), + Map.Entry::getValue + )); + } + + public JsonObject getPayload() { + return this.payload; + } + + public byte[] encode() { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + try (Writer writer = new OutputStreamWriter(new GZIPOutputStream(bytesOut), StandardCharsets.UTF_8)) { + GsonProvider.normal().toJson(this.payload, writer); + } catch (IOException e) { + e.printStackTrace(); + } + return bytesOut.toByteArray(); + } + + public Map> getHolders() { + return this.holders; + } + + public Map> getTracks() { + return this.tracks; + } + /** * Generates a web editor request payload. * @@ -95,34 +136,35 @@ public static WebEditorRequest generate(List holders, List> holdersMap = holders.stream().collect(ImmutableCollectors.toMap( + Function.identity(), + holder -> holder.normalData().asList() + )); - /** - * The encoded json object this payload is made up of - */ - private final JsonObject payload; + Map> tracksMap = tracks.stream().collect(ImmutableCollectors.toMap( + Function.identity(), + Track::getGroups + )); + + JsonObject json = createJsonPayload(holdersMap, tracksMap, sender, cmdLabel, potentialContexts.build(), plugin).toJson(); + return new WebEditorRequest(json, holdersMap, tracksMap); + } - private WebEditorRequest(List holders, List tracks, Sender sender, String cmdLabel, ImmutableContextSet potentialContexts, LuckPermsPlugin plugin) { - this.payload = new JObject() + private static JObject createJsonPayload(Map> holders, Map> tracks, Sender sender, String cmdLabel, ImmutableContextSet potentialContexts, LuckPermsPlugin plugin) { + return new JObject() .add("metadata", formMetadata(sender, cmdLabel, plugin.getBootstrap().getVersion())) - .add("permissionHolders", new JArray() - .consume(arr -> { - for (PermissionHolder holder : holders) { - arr.add(formPermissionHolder(holder)); - } - }) - ) - .add("tracks", new JArray() - .consume(arr -> { - for (Track track : tracks) { - arr.add(formTrack(track)); - } - }) - ) + .add("permissionHolders", new JArray().consume(arr -> + holders.forEach((holder, data) -> + arr.add(formPermissionHolder(holder, data)) + ) + )) + .add("tracks", new JArray().consume(arr -> + tracks.forEach((track, data) -> + arr.add(formTrack(track, data)) + ) + )) .add("knownPermissions", new JArray().addAll(plugin.getPermissionRegistry().rootAsList())) - .add("potentialContexts", ContextSetJsonSerializer.serialize(potentialContexts)) - .toJson(); + .add("potentialContexts", ContextSetJsonSerializer.serialize(potentialContexts)); } private static JObject formMetadata(Sender sender, String cmdLabel, String pluginVersion) { @@ -136,56 +178,19 @@ private static JObject formMetadata(Sender sender, String cmdLabel, String plugi .add("pluginVersion", pluginVersion); } - private static JObject formPermissionHolder(PermissionHolder holder) { + private static JObject formPermissionHolder(PermissionHolder holder, List data) { return new JObject() .add("type", holder.getType().toString()) .add("id", holder.getIdentifier().getName()) .add("displayName", holder.getPlainDisplayName()) - .add("nodes", NodeJsonSerializer.serializeNodes(holder.normalData().asList())); + .add("nodes", NodeJsonSerializer.serializeNodes(data)); } - private static JObject formTrack(Track track) { + private static JObject formTrack(Track track, List data) { return new JObject() .add("type", "track") .add("id", track.getName()) - .add("groups", new JArray().addAll(track.getGroups())); - } - - public byte[] encode() { - ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - try (Writer writer = new OutputStreamWriter(new GZIPOutputStream(bytesOut), StandardCharsets.UTF_8)) { - GsonProvider.normal().toJson(this.payload, writer); - } catch (IOException e) { - e.printStackTrace(); - } - return bytesOut.toByteArray(); - } - - /** - * Creates a web editor session, and sends the URL to the sender. - * - * @param plugin the plugin - * @param sender the sender creating the session - * @return the command result - */ - public void createSession(LuckPermsPlugin plugin, Sender sender) { - String pasteId; - try { - pasteId = plugin.getBytebin().postContent(encode(), AbstractHttpClient.JSON_TYPE).key(); - } catch (UnsuccessfulRequestException e) { - Message.EDITOR_HTTP_REQUEST_FAILURE.send(sender, e.getResponse().code(), e.getResponse().message()); - return; - } catch (IOException e) { - new RuntimeException("Error uploading data to bytebin", e).printStackTrace(); - Message.EDITOR_HTTP_UNKNOWN_FAILURE.send(sender); - return; - } - - plugin.getWebEditorSessionStore().addNewSession(pasteId); - - // form a url for the editor - String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + pasteId; - Message.EDITOR_URL.send(sender, url); + .add("groups", new JArray().addAll(data)); } public static void includeMatchingGroups(List holders, Predicate filter, LuckPermsPlugin plugin) { diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java index b90bcd967..8cd9eb054 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorResponse.java @@ -40,11 +40,12 @@ import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.manager.group.GroupManager; -import me.lucko.luckperms.common.model.nodemap.MutateResult; import me.lucko.luckperms.common.node.utils.NodeJsonSerializer; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.Difference; import me.lucko.luckperms.common.util.Uuids; +import me.lucko.luckperms.common.webeditor.store.RemoteSession; import net.kyori.adventure.text.Component; import net.luckperms.api.actionlog.Action; @@ -55,7 +56,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.UUID; @@ -86,41 +86,31 @@ public WebEditorResponse(String id, JsonObject payload) { * @param plugin the plugin * @param sender the sender who is applying the session */ - public void apply(LuckPermsPlugin plugin, Sender sender, String commandLabel, boolean ignoreSessionWarning) { - JsonElement sessionIdJson = this.payload.get("sessionId"); - if (sessionIdJson != null) { - String sessionId = sessionIdJson.getAsString(); - WebEditorSessionStore sessionStore = plugin.getWebEditorSessionStore(); - - SessionState state = sessionStore.getSessionState(sessionId); - switch (state) { - case COMPLETED: - if (!ignoreSessionWarning) { - Message.APPLY_EDITS_SESSION_APPLIED_ALREADY.send(sender, this.id, commandLabel); - return; - } - break; - case NOT_KNOWN: - if (!ignoreSessionWarning) { - Message.APPLY_EDITS_SESSION_UNKNOWN.send(sender, this.id, commandLabel); - return; - } - break; - case IN_PROGRESS: - sessionStore.markSessionCompleted(sessionId); - break; - default: - throw new AssertionError(state); + public void apply(LuckPermsPlugin plugin, Sender sender, WebEditorSession editorSession, String commandLabel, boolean ignoreSessionWarning) { + String sessionId = this.payload.get("sessionId").getAsString(); + RemoteSession remoteSession = plugin.getWebEditorStore().sessions().getSession(sessionId); + + if (remoteSession == null) { + // session is unknown + if (!ignoreSessionWarning) { + Message.APPLY_EDITS_SESSION_UNKNOWN.send(sender, this.id, commandLabel); + return; + } + } else if (remoteSession.isCompleted()) { + // session has been completed already + if (!ignoreSessionWarning) { + Message.APPLY_EDITS_SESSION_APPLIED_ALREADY.send(sender, this.id, commandLabel); + return; } } - Session session = new Session(plugin, sender); + ChangeApplier changeApplier = new ChangeApplier(plugin, sender, editorSession, remoteSession); boolean work = false; if (this.payload.has("changes")) { JsonArray changes = this.payload.get("changes").getAsJsonArray(); for (JsonElement change : changes) { - if (session.applyChange(change.getAsJsonObject())) { + if (changeApplier.applyChange(change.getAsJsonObject())) { work = true; } } @@ -128,7 +118,7 @@ public void apply(LuckPermsPlugin plugin, Sender sender, String commandLabel, bo if (this.payload.has("userDeletions")) { JsonArray userDeletions = this.payload.get("userDeletions").getAsJsonArray(); for (JsonElement userDeletion : userDeletions) { - if (session.applyUserDelete(userDeletion)) { + if (changeApplier.applyUserDelete(userDeletion)) { work = true; } } @@ -136,7 +126,7 @@ public void apply(LuckPermsPlugin plugin, Sender sender, String commandLabel, bo if (this.payload.has("groupDeletions")) { JsonArray groupDeletions = this.payload.get("groupDeletions").getAsJsonArray(); for (JsonElement groupDeletion : groupDeletions) { - if (session.applyGroupDelete(groupDeletion)) { + if (changeApplier.applyGroupDelete(groupDeletion)) { work = true; } } @@ -144,12 +134,16 @@ public void apply(LuckPermsPlugin plugin, Sender sender, String commandLabel, bo if (this.payload.has("trackDeletions")) { JsonArray trackDeletions = this.payload.get("trackDeletions").getAsJsonArray(); for (JsonElement trackDeletion : trackDeletions) { - if (session.applyTrackDelete(trackDeletion)) { + if (changeApplier.applyTrackDelete(trackDeletion)) { work = true; } } } + if (remoteSession != null) { + remoteSession.complete(); + } + if (!work) { Message.APPLY_EDITS_TARGET_NO_CHANGES_PRESENT.send(sender); } @@ -158,13 +152,17 @@ public void apply(LuckPermsPlugin plugin, Sender sender, String commandLabel, bo /** * Represents the application of a given editor session on this platform. */ - private static class Session { + private static class ChangeApplier { private final LuckPermsPlugin plugin; private final Sender sender; + private final WebEditorSession session; + private final RemoteSession remoteSession; - Session(LuckPermsPlugin plugin, Sender sender) { + ChangeApplier(LuckPermsPlugin plugin, Sender sender, WebEditorSession session, RemoteSession remoteSession) { this.plugin = plugin; this.sender = sender; + this.session = session; + this.remoteSession = remoteSession; } private boolean applyChange(JsonObject changeInfo) { @@ -202,6 +200,9 @@ private boolean applyHolderChange(JsonObject changeInfo) { holder = this.plugin.getStorage().loadGroup(id).join().orElse(null); if (holder == null) { holder = this.plugin.getStorage().createAndLoadGroup(id, CreationCause.WEB_EDITOR).join(); + if (this.session != null) { + this.session.includeCreatedGroup((Group) holder); + } } } @@ -211,7 +212,7 @@ private boolean applyHolderChange(JsonObject changeInfo) { } Set nodes = NodeJsonSerializer.deserializeNodes(changeInfo.getAsJsonArray("nodes")); - MutateResult res = holder.setNodes(DataType.NORMAL, nodes, true); + Difference res = applyNodeChanges(holder, nodes); if (res.isEmpty()) { return false; @@ -233,22 +234,51 @@ private boolean applyHolderChange(JsonObject changeInfo) { Message.APPLY_EDITS_SUCCESS.send(this.sender, type, holder.getFormattedDisplayName()); Message.APPLY_EDITS_SUCCESS_SUMMARY.send(this.sender, added.size(), removed.size()); + for (Node n : added) { Message.APPLY_EDITS_DIFF_ADDED.send(this.sender, n); } for (Node n : removed) { Message.APPLY_EDITS_DIFF_REMOVED.send(this.sender, n); } + StorageAssistant.save(holder, this.sender, this.plugin); return true; } + private Difference applyNodeChanges(PermissionHolder holder, Set nodes) { + if (this.remoteSession != null) { + + WebEditorRequest request = this.remoteSession.request(); + if (request != null) { + + List nodesBefore = request.getHolders().get(holder.getIdentifier()); + if (nodesBefore != null) { + + // if the initial data sent to the remote session is still known + // use that to calculate a diff of the changes made to avoid overriding + // modified/added/removed nodes since the editor session was created + Difference diff = new Difference<>(); + diff.recordChanges(Difference.ChangeType.REMOVE, nodesBefore); + diff.recordChanges(Difference.ChangeType.ADD, nodes); + + return holder.setNodes(DataType.NORMAL, diff, true); + } + } + } + + return holder.setNodes(DataType.NORMAL, nodes, true); + } + private boolean applyTrackChange(JsonObject changeInfo) { String id = changeInfo.get("id").getAsString(); Track track = this.plugin.getStorage().loadTrack(id).join().orElse(null); if (track == null) { track = this.plugin.getStorage().createAndLoadTrack(id, CreationCause.WEB_EDITOR).join(); + if (this.session != null) { + this.session.includeCreatedTrack(track); + } } if (ArgumentPermissions.checkModifyPerms(this.plugin, this.sender, CommandPermission.APPLY_EDITS, track)) { @@ -264,32 +294,33 @@ private boolean applyTrackChange(JsonObject changeInfo) { return false; } - Set diffAdded = getAdded(before, after); - Set diffRemoved = getRemoved(before, after); + Difference diff = new Difference<>(); + diff.recordChanges(Difference.ChangeType.REMOVE, before); + diff.recordChanges(Difference.ChangeType.ADD, after); - int additions = diffAdded.size(); - int deletions = diffRemoved.size(); + Set added = diff.getAdded(); + Set removed = diff.getRemoved(); track.setGroups(after); - if (hasBeenReordered(before, after, diffAdded, diffRemoved)) { + if (hasBeenReordered(before, after, added, removed)) { LoggedAction.build().source(this.sender).target(track) .description("webeditor", "reorder", after) .build().submit(this.plugin, this.sender); } - for (String n : diffAdded) { + for (String n : added) { LoggedAction.build().source(this.sender).target(track) .description("webeditor", "add", n) .build().submit(this.plugin, this.sender); } - for (String n : diffRemoved) { + for (String n : removed) { LoggedAction.build().source(this.sender).target(track) .description("webeditor", "remove", n) .build().submit(this.plugin, this.sender); } Message.APPLY_EDITS_SUCCESS.send(this.sender, "track", Component.text(track.getName())); - Message.APPLY_EDITS_SUCCESS_SUMMARY.send(this.sender, additions, deletions); + Message.APPLY_EDITS_SUCCESS_SUMMARY.send(this.sender, added.size(), removed.size()); Message.APPLY_EDITS_TRACK_BEFORE.send(this.sender, before); Message.APPLY_EDITS_TRACK_AFTER.send(this.sender, after); @@ -339,6 +370,10 @@ private boolean applyUserDelete(JsonElement changeInfo) { .description("webeditor", "delete") .build().submit(this.plugin, this.sender); + if (this.session != null) { + this.session.excludeDeletedUser(user); + } + return true; } @@ -374,6 +409,10 @@ private boolean applyGroupDelete(JsonElement changeInfo) { .description("webeditor", "delete") .build().submit(this.plugin, this.sender); + if (this.session != null) { + this.session.excludeDeletedGroup(group); + } + return true; } @@ -404,19 +443,11 @@ private boolean applyTrackDelete(JsonElement changeInfo) { .description("webeditor", "delete") .build().submit(this.plugin, this.sender); - return true; - } - - private static Set getAdded(Collection before, Collection after) { - Set added = new LinkedHashSet<>(after); - added.removeAll(before); - return added; - } + if (this.session != null) { + this.session.excludeDeletedTrack(track); + } - private static Set getRemoved(Collection before, Collection after) { - Set removed = new LinkedHashSet<>(before); - removed.removeAll(after); - return removed; + return true; } private static boolean hasBeenReordered(List before, List after, Collection diffAdded, Collection diffRemoved) { diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java new file mode 100644 index 000000000..cb29b1bbd --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java @@ -0,0 +1,206 @@ +/* + * 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.webeditor; + +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.http.AbstractHttpClient; +import me.lucko.luckperms.common.http.UnsuccessfulRequestException; +import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.common.model.Group; +import me.lucko.luckperms.common.model.PermissionHolder; +import me.lucko.luckperms.common.model.PermissionHolderIdentifier; +import me.lucko.luckperms.common.model.Track; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * Encapsulates a session with the web editor. + * + *

A session is tied to a specific user, and can comprise of multiple requests to and + * responses from the web editor.

+ */ +public class WebEditorSession { + + public static void createAndOpen(List holders, List tracks, Sender sender, String cmdLabel, LuckPermsPlugin plugin) { + WebEditorRequest initialRequest = WebEditorRequest.generate(holders, tracks, sender, cmdLabel, plugin); + WebEditorSession session = new WebEditorSession(initialRequest, plugin, sender, cmdLabel); + session.open(); + } + + private WebEditorRequest initialRequest; + + private final LuckPermsPlugin plugin; + private final Sender sender; + private final String cmdLabel; + + private final Set holders; + private final Set tracks; + + private WebEditorSocket socket = null; + + public WebEditorSession(WebEditorRequest initialRequest, LuckPermsPlugin plugin, Sender sender, String cmdLabel) { + this.initialRequest = initialRequest; + this.plugin = plugin; + this.sender = sender; + this.cmdLabel = cmdLabel; + + this.holders = new HashSet<>(initialRequest.getHolders().keySet()); + this.tracks = new HashSet<>(initialRequest.getTracks().keySet()); + } + + public void open() { + createSocket(); + createInitialSession(); + } + + private void createSocket() { + try { + // create and connect to a socket + WebEditorSocket socket = new WebEditorSocket(this.plugin, this.sender, this); + socket.initialize(this.plugin.getBytesocks()); + socket.waitForConnect(5, TimeUnit.SECONDS); + + this.socket = socket; + this.plugin.getWebEditorStore().sockets().putSocket(this.sender, this.socket); + } catch (Exception e) { + if (e instanceof UnsuccessfulRequestException && ((UnsuccessfulRequestException) e).getResponse().code() == 502) { + // 502 - bad gateway, probably means the socket service is offline + // that's ok, no need to send a warning + return; + } + + this.plugin.getLogger().warn("Unable to establish socket connection", e); + } + } + + private void createInitialSession() { + Objects.requireNonNull(this.initialRequest); + + WebEditorRequest request = this.initialRequest; + this.initialRequest = null; + + if (this.socket != null) { + this.socket.appendDetailToRequest(request); + } + + String id = uploadRequestData(request); + if (id == null) { + return; + } + + // form a url for the editor + String url = this.plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + id; + Message.EDITOR_URL.send(this.sender, url); + + // schedule socket close + if (this.socket != null) { + this.socket.scheduleCleanupIfUnused(); + } + } + + public void includeCreatedGroup(Group group) { + this.holders.add(group.getIdentifier()); + } + + public void includeCreatedTrack(Track track) { + this.tracks.add(track.getName()); + } + + public void excludeDeletedUser(User user) { + this.holders.remove(user.getIdentifier()); + } + + public void excludeDeletedGroup(Group group) { + this.holders.remove(group.getIdentifier()); + } + + public void excludeDeletedTrack(Track track) { + this.tracks.remove(track.getName()); + } + + public String createFollowUpSession() { + List holders = this.holders.stream() + .map(id -> { + switch (id.getType()) { + case PermissionHolderIdentifier.USER_TYPE: + return this.plugin.getStorage().loadUser(UUID.fromString(id.getName()), null); + case PermissionHolderIdentifier.GROUP_TYPE: + return this.plugin.getStorage().loadGroup(id.getName()).thenApply(o -> o.orElse(null)); + default: + return null; + } + }) + .filter(Objects::nonNull) + .map(CompletableFuture::join) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + List tracks = this.tracks.stream() + .map(id -> this.plugin.getStorage().loadTrack(id).thenApply(o -> o.orElse(null))) + .map(CompletableFuture::join) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + return uploadRequestData(WebEditorRequest.generate(holders, tracks, this.sender, this.cmdLabel, this.plugin)); + } + + public String getCommandLabel() { + return this.cmdLabel; + } + + private String uploadRequestData(WebEditorRequest request) { + byte[] requestBuf = request.encode(); + + String pasteId; + try { + pasteId = this.plugin.getBytebin().postContent(requestBuf, AbstractHttpClient.JSON_TYPE, "editor").key(); + } catch (UnsuccessfulRequestException e) { + Message.EDITOR_HTTP_REQUEST_FAILURE.send(this.sender, e.getResponse().code(), e.getResponse().message()); + return null; + } catch (IOException e) { + new RuntimeException("Error uploading data to bytebin", e).printStackTrace(); + Message.EDITOR_HTTP_UNKNOWN_FAILURE.send(this.sender); + return null; + } + + this.plugin.getWebEditorStore().sessions().addNewSession(pasteId, request); + return pasteId; + } + + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/CryptographyUtils.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/CryptographyUtils.java new file mode 100644 index 000000000..658c3b4da --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/CryptographyUtils.java @@ -0,0 +1,118 @@ +/* + * 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.webeditor.socket; + +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +/** + * Utilities for public/private key crypto used by the web editor socket connection. + */ +public final class CryptographyUtils { + private CryptographyUtils() {} + + /** + * Parse a public key from the given string. + * + * @param base64String a base64 string encoding the public key + * @return the parsed public key + * @throws IllegalArgumentException if the input was invalid + */ + public static PublicKey parsePublicKey(String base64String) throws IllegalArgumentException { + try { + byte[] bytes = Base64.getDecoder().decode(base64String); + X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes); + KeyFactory rsa = KeyFactory.getInstance("RSA"); + return rsa.generatePublic(spec); + } catch (Exception e) { + throw new IllegalArgumentException("Exception parsing public key", e); + } + } + + /** + * Generate a public/private key pair. + * + * @return the generated key pair + */ + public static KeyPair generateKeyPair() { + try { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(4096); + return generator.generateKeyPair(); + } catch (Exception e) { + throw new RuntimeException("Exception generating keypair", e); + } + } + + /** + * Signs {@code msg} using the given {@link PrivateKey}. + * + * @param privateKey the private key to sign with + * @param msg the message + * @return a base64 string containing the signature + */ + public static String sign(PrivateKey privateKey, String msg) { + try { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(privateKey); + sign.update(msg.getBytes(StandardCharsets.UTF_8)); + + return Base64.getEncoder().encodeToString(sign.sign()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Verify that the given base64 encoded signature matches + * the given message and {@link PublicKey}. + * + * @param publicKey the public key that the message was supposedly signed with + * @param msg the message + * @param signatureBase64 the provided signature + * @return true if the signature is ok + */ + public static boolean verify(PublicKey publicKey, String msg, String signatureBase64) { + try { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initVerify(publicKey); + sign.update(msg.getBytes(StandardCharsets.UTF_8)); + + byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64); + return sign.verify(signatureBytes); + } catch (Exception e) { + return false; + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/SocketMessageType.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/SocketMessageType.java new file mode 100644 index 000000000..2b116ad09 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/SocketMessageType.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.common.webeditor.socket; + +import me.lucko.luckperms.common.util.ImmutableCollectors; +import me.lucko.luckperms.common.util.gson.JObject; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; + +public enum SocketMessageType { + + /** Sent when the editor first says "hello" over the channel. (editor -> plugin) */ + HELLO("hello"), + + /** Sent when the plugin replies to the editors "hello" message. (plugin -> editor) */ + HELLO_REPLY("hello-reply"), + + /** Sent by the editor to confirm that a connection has been established. (editor -> plugin) */ + CONNECTED("connected"), + + /** Sent by the editor to request that the plugin applies a change. (editor -> plugin) */ + CHANGE_REQUEST("change-request"), + + /** Sent by the plugin to confirm that the changes sent by the editor have been accepted or applied. (plugin -> editor) */ + CHANGE_RESPONSE("change-response"), + + /** Ping message to keep the socket alive. (editor -> plugin) */ + PING("ping"), + + /** Ping response. (plugin -> editor) */ + PONG("pong"); + + public final String id; + + SocketMessageType(String id) { + this.id = id; + } + + public JObject builder() { + return new JObject().add("type", this.id); + } + + private static final Map LOOKUP = Arrays.stream(SocketMessageType.values()) + .collect(ImmutableCollectors.toMap(m -> m.id, Function.identity())); + + public static SocketMessageType getById(String id) { + SocketMessageType type = LOOKUP.get(id); + if (type == null) { + throw new IllegalArgumentException(id); + } + return type; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java new file mode 100644 index 000000000..aaed889e4 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java @@ -0,0 +1,258 @@ +/* + * 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.webeditor.socket; + +import com.google.gson.JsonObject; + +import me.lucko.luckperms.common.http.BytesocksClient; +import me.lucko.luckperms.common.http.UnsuccessfulRequestException; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask; +import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.gson.GsonProvider; +import me.lucko.luckperms.common.util.gson.JObject; +import me.lucko.luckperms.common.webeditor.WebEditorRequest; +import me.lucko.luckperms.common.webeditor.WebEditorSession; +import me.lucko.luckperms.common.webeditor.socket.listener.WebEditorSocketListener; + +import java.io.IOException; +import java.security.KeyPair; +import java.security.PublicKey; +import java.util.Base64; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class WebEditorSocket { + + private static final int PROTOCOL_VERSION = 1; + + /** The plugin */ + private final LuckPermsPlugin plugin; + /** The sender who created the editor session */ + private final Sender sender; + /** The web editor session */ + private final WebEditorSession session; + /** The socket listener that handles incoming messages */ + private final WebEditorSocketListener listener; + + /** The websocket backing the connection */ + private BytesocksClient.Socket socket; + /** The public and private keys used to sign messages sent by the plugin */ + private KeyPair localKeys; + /** A task to check if the socket is still active */ + private SchedulerTask keepaliveTask; + /** The public key used by the editor to sign messages */ + private PublicKey remotePublicKey; + /** If the connection is closed */ + private boolean closed = false; + + public WebEditorSocket(LuckPermsPlugin plugin, Sender sender, WebEditorSession session) { + this.plugin = plugin; + this.sender = sender; + this.session = session; + this.listener = new WebEditorSocketListener(this); + } + + /** + * Initializes the socket connection. + * + * @param client the bytesocks client to connect to + * @throws UnsuccessfulRequestException if the request fails + * @throws IOException if an i/o error occurs + */ + public void initialize(BytesocksClient client) throws UnsuccessfulRequestException, IOException { + this.socket = client.createSocket(this.listener); + this.localKeys = CryptographyUtils.generateKeyPair(); + } + + /** + * Waits the specified amount of time for the socket to connect, + * before throwing an exception if a timeout occurs. + * + * @param timeout the timeout + * @param unit the timeout unit + */ + public void waitForConnect(long timeout, TimeUnit unit) { + try { + this.listener.connectFuture().get(timeout, unit); + } catch (ExecutionException | TimeoutException | InterruptedException e) { + throw new RuntimeException("Timed out waiting to socket to connect", e); + } + } + + /** + * Adds detail about the socket channel and the plugin public key to + * the editor request payload that gets sent via bytebin to the viewer. + * + * @param request the request + */ + public void appendDetailToRequest(WebEditorRequest request) { + String channelId = this.socket.channelId(); + String publicKey = Base64.getEncoder().encodeToString(this.localKeys.getPublic().getEncoded()); + + JsonObject socket = new JsonObject(); + socket.addProperty("protocolVersion", PROTOCOL_VERSION); + socket.addProperty("channelId", channelId); + socket.addProperty("publicKey", publicKey); + + JsonObject payload = request.getPayload(); + payload.add("socket", socket); + } + + /** + * Send a message to the socket. + * + *

The message will be encoded as JSON and + * signed using the public public key.

+ * + * @param msg the message + */ + public void send(JsonObject msg) { + String encoded = GsonProvider.normal().toJson(msg); + String signature = CryptographyUtils.sign(this.localKeys.getPrivate(), encoded); + + JsonObject frame = new JObject() + .add("msg", encoded) + .add("signature", signature) + .toJson(); + + this.socket.socket().send(GsonProvider.normal().toJson(frame)); + } + + public boolean trustConnection(String nonce) { + if (this.listener.shouldIgnoreMessages()) { + return false; + } + + if (this.remotePublicKey != null) { + return false; + } + + PublicKey publicKey = this.listener.helloHandler().getAttemptedConnection(nonce); + if (publicKey == null) { + return false; + } + + this.remotePublicKey = publicKey; + + // save the key in the keystore + this.plugin.getWebEditorStore().keystore().trust(this.sender, this.remotePublicKey.getEncoded()); + + // send a reply back to the editor to say that it is now trusted + send(SocketMessageType.HELLO_REPLY.builder() + .add("nonce", nonce) + .add("state", "trusted") + .toJson() + ); + return true; + } + + public void scheduleCleanupIfUnused() { + this.plugin.getBootstrap().getScheduler().asyncLater(this::afterOpenFor1Minute, 1, TimeUnit.MINUTES); + } + + private void afterOpenFor1Minute() { + if (this.closed) { + return; + } + + if (this.remotePublicKey == null && !this.listener.helloHandler().hasAttemptedConnection()) { + // If the editor hasn't made an initial connection after 1 minute, + // then close + stop listening to the socket. + closeSocket(); + } else { + // Otherwise, setup a keepalive monitoring task + this.keepaliveTask = this.plugin.getBootstrap().getScheduler().asyncRepeating(this::keepalive, 10, TimeUnit.SECONDS); + } + } + + /** + * The keepalive tasks checks to see when the last ping from the editor was. If the editor + * hasn't sent anything for 1 minute, then close the connection + */ + private void keepalive() { + if (System.currentTimeMillis() - this.listener.pingHandler().getLastPing() > TimeUnit.MINUTES.toMillis(1)) { + cancelKeepalive(); + closeSocket(); + } + } + + public void close() { + try { + send(SocketMessageType.PONG.builder() + .add("ok", false) + .toJson() + ); + } catch (Exception e) { + // ignore + } + + cancelKeepalive(); + closeSocket(); + } + + private void closeSocket() { + this.socket.socket().close(1000, "Normal"); + this.closed = true; + } + + private void cancelKeepalive() { + if (this.keepaliveTask != null) { + this.keepaliveTask.cancel(); + this.keepaliveTask = null; + } + } + + public LuckPermsPlugin getPlugin() { + return this.plugin; + } + + public Sender getSender() { + return this.sender; + } + + public WebEditorSession getSession() { + return this.session; + } + + public BytesocksClient.Socket getSocket() { + return this.socket; + } + + public PublicKey getRemotePublicKey() { + return this.remotePublicKey; + } + + public void setRemotePublicKey(PublicKey remotePublicKey) { + this.remotePublicKey = remotePublicKey; + } + + public boolean isClosed() { + return this.closed; + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/Handler.java similarity index 75% rename from common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java rename to common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/Handler.java index a99574357..e1170f214 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/SessionState.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/Handler.java @@ -23,26 +23,15 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.webeditor; +package me.lucko.luckperms.common.webeditor.socket.listener; + +import com.google.gson.JsonObject; /** - * Represents the state of a web editor session + * A handler for a given type of message. */ -enum SessionState { - - /** - * The session is not known to this server. - */ - NOT_KNOWN, - - /** - * The session is in progress - it has been created, but updates have not been applied. - */ - IN_PROGRESS, +public interface Handler { - /** - * The session is complete - updates have been applied. - */ - COMPLETED + void handle(JsonObject msg); } diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerChangeRequest.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerChangeRequest.java new file mode 100644 index 000000000..9ccf5fb50 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerChangeRequest.java @@ -0,0 +1,105 @@ +/* + * 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.webeditor.socket.listener; + +import com.google.gson.JsonObject; + +import me.lucko.luckperms.common.command.access.CommandPermission; +import me.lucko.luckperms.common.http.UnsuccessfulRequestException; +import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.common.webeditor.WebEditorResponse; +import me.lucko.luckperms.common.webeditor.socket.SocketMessageType; +import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; + +import java.io.IOException; +import java.util.Objects; + +/** + * Handler for {@link SocketMessageType#CHANGE_REQUEST} + */ +public class HandlerChangeRequest implements Handler { + + /** The change has been accepted, and the plugin will now apply it. */ + private static final String STATE_ACCEPTED = "accepted"; + /** The change has been applied. */ + private static final String STATE_APPLIED = "applied"; + + /** The socket */ + private final WebEditorSocket socket; + + public HandlerChangeRequest(WebEditorSocket socket) { + this.socket = socket; + } + + @Override + public void handle(JsonObject msg) { + if (!this.socket.getSender().hasPermission(CommandPermission.APPLY_EDITS)) { + throw new IllegalStateException("Sender does not have applyedits permission"); + } + + // get the bytebin code containing the editor data + String code = msg.get("code").getAsString(); + if (code == null || code.isEmpty()) { + throw new IllegalArgumentException("Invalid code"); + } + + // send "change-accepted" response + this.socket.getPlugin().getBootstrap().getScheduler().executeAsync(() -> + this.socket.send(SocketMessageType.CHANGE_RESPONSE.builder() + .add("state", STATE_ACCEPTED) + .toJson() + ) + ); + + // download data from bytebin + JsonObject data; + try { + data = this.socket.getPlugin().getBytebin().getJsonContent(code).getAsJsonObject(); + Objects.requireNonNull(data); + } catch (UnsuccessfulRequestException | IOException e) { + throw new RuntimeException("Error reading data", e); + } + + // apply changes + Message.EDITOR_SOCKET_CHANGES_RECEIVED.send(this.socket.getSender()); + new WebEditorResponse(code, data).apply( + this.socket.getPlugin(), + this.socket.getSender(), + this.socket.getSession(), + "lp", + false + ); + + // create a new session + String newSessionCode = this.socket.getSession().createFollowUpSession(); + this.socket.send(SocketMessageType.CHANGE_RESPONSE.builder() + .add("state", STATE_APPLIED) + .add("newSessionCode", newSessionCode) + .toJson() + ); + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerConnected.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerConnected.java new file mode 100644 index 000000000..457d62913 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerConnected.java @@ -0,0 +1,50 @@ +/* + * 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.webeditor.socket.listener; + +import com.google.gson.JsonObject; + +import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.common.webeditor.socket.SocketMessageType; +import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; + +/** + * Handler for {@link SocketMessageType#CONNECTED} + */ +public class HandlerConnected implements Handler { + + /** The socket */ + private final WebEditorSocket socket; + + public HandlerConnected(WebEditorSocket socket) { + this.socket = socket; + } + + @Override + public void handle(JsonObject msg) { + Message.EDITOR_SOCKET_CONNECTED.send(this.socket.getSender()); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerHello.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerHello.java new file mode 100644 index 000000000..a5c239004 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerHello.java @@ -0,0 +1,130 @@ +/* + * 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.webeditor.socket.listener; + +import com.google.gson.JsonObject; + +import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.common.webeditor.socket.CryptographyUtils; +import me.lucko.luckperms.common.webeditor.socket.SocketMessageType; +import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; +import me.lucko.luckperms.common.webeditor.store.RemoteSession; + +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Map; + +/** + * Handler for {@link SocketMessageType#HELLO} + */ +public class HandlerHello implements Handler { + + /** The session is accepted, the editor public key is already known so no further action is needed */ + private static final String STATE_ACCEPTED = "accepted"; + /** The session is accepted, but the user needs to confirm in-game before any changes will be accepted. */ + private static final String STATE_UNTRUSTED = "untrusted"; + /** A session has already been established with a different identity */ + private static final String STATE_REJECTED = "rejected"; + /** The remote editor session is based off session data which has already been applied */ + private static final String STATE_INVALID = "invalid"; + + /** The socket */ + private final WebEditorSocket socket; + + /** A list of attempted connections (connections that have been attempted with an untrusted public key) */ + private final Map attemptedConnections = new HashMap<>(); + + public HandlerHello(WebEditorSocket socket) { + this.socket = socket; + } + + public PublicKey getAttemptedConnection(String nonce) { + return this.attemptedConnections.get(nonce); + } + + public boolean hasAttemptedConnection() { + return !this.attemptedConnections.isEmpty(); + } + + @Override + public void handle(JsonObject msg) { + String nonce = getStringOrThrow(msg, "nonce"); + String sessionId = getStringOrThrow(msg, "sessionId"); + String browser = msg.get("browser").getAsString(); + PublicKey remotePublicKey = CryptographyUtils.parsePublicKey(msg.get("publicKey").getAsString()); + + // check if the public keys are the same + // (this allows the same editor to re-connect, but prevents new connections) + if (this.socket.getRemotePublicKey() != null && !this.socket.getRemotePublicKey().equals(remotePublicKey)) { + sendReply(nonce, STATE_REJECTED); + return; + } + + // check if session is valid + RemoteSession session = this.socket.getPlugin().getWebEditorStore().sessions().getSession(sessionId); + if (session == null || session.isCompleted()) { + sendReply(nonce, STATE_INVALID); + return; + } + + // check if the public key is trusted + if (!this.socket.getPlugin().getWebEditorStore().keystore().isTrusted(this.socket.getSender(), remotePublicKey.getEncoded())) { + sendReply(nonce, STATE_UNTRUSTED); + + // ask the user if they want to trust the connection + Message.EDITOR_SOCKET_UNTRUSTED.send(this.socket.getSender(), nonce, browser, this.socket.getSession().getCommandLabel(), this.socket.getSender().isConsole()); + this.attemptedConnections.put(nonce, remotePublicKey); + return; + } + + boolean reconnected = this.socket.getRemotePublicKey() != null; + this.socket.setRemotePublicKey(remotePublicKey); + + sendReply(nonce, STATE_ACCEPTED); + + if (reconnected) { + Message.EDITOR_SOCKET_RECONNECTED.send(this.socket.getSender()); + } else { + Message.EDITOR_SOCKET_CONNECTED.send(this.socket.getSender()); + } + } + + private void sendReply(String nonce, String state) { + this.socket.send(SocketMessageType.HELLO_REPLY.builder() + .add("nonce", nonce) + .add("state", state) + .toJson() + ); + } + + private static String getStringOrThrow(JsonObject msg, String key) { + String val = msg.get(key).getAsString(); + if (val == null || val.isEmpty()) { + throw new IllegalStateException("missing " + key); + } + return val; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerPing.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerPing.java new file mode 100644 index 000000000..68fe75549 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/HandlerPing.java @@ -0,0 +1,60 @@ +/* + * 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.webeditor.socket.listener; + +import com.google.gson.JsonObject; + +import me.lucko.luckperms.common.webeditor.socket.SocketMessageType; +import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; + +/** + * Handler for {@link SocketMessageType#PING} + */ +public class HandlerPing implements Handler { + + /** The socket */ + private final WebEditorSocket socket; + + /** The time a ping was last received */ + private long lastPing = 0; + + public HandlerPing(WebEditorSocket socket) { + this.socket = socket; + } + + public long getLastPing() { + return this.lastPing; + } + + @Override + public void handle(JsonObject msg) { + this.lastPing = System.currentTimeMillis(); + this.socket.send(SocketMessageType.PONG.builder() + .add("ok", true) + .toJson() + ); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/WebEditorSocketListener.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/WebEditorSocketListener.java new file mode 100644 index 000000000..e50676ed1 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/listener/WebEditorSocketListener.java @@ -0,0 +1,181 @@ +/* + * 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.webeditor.socket.listener; + +import com.google.gson.JsonObject; + +import me.lucko.luckperms.common.util.gson.GsonProvider; +import me.lucko.luckperms.common.webeditor.socket.CryptographyUtils; +import me.lucko.luckperms.common.webeditor.socket.SocketMessageType; +import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; + +import java.io.EOFException; +import java.security.PublicKey; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.ReentrantLock; + +public class WebEditorSocketListener extends WebSocketListener { + + /** The socket */ + private final WebEditorSocket socket; + + // Individual handlers for each message type + private final HandlerHello helloHandler; + private final HandlerConnected connectedHandler; + private final HandlerPing pingHandler; + private final HandlerChangeRequest changeRequestHandler; + + /** A future that will complete when the connection is established successfully */ + private final CompletableFuture connectFuture = new CompletableFuture<>(); + + /** Message receive lock */ + private final ReentrantLock lock = new ReentrantLock(); + + public WebEditorSocketListener(WebEditorSocket socket) { + this.socket = socket; + this.helloHandler = new HandlerHello(socket); + this.connectedHandler = new HandlerConnected(socket); + this.pingHandler = new HandlerPing(socket); + this.changeRequestHandler = new HandlerChangeRequest(socket); + } + + @Override + public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { + this.connectFuture.complete(null); + } + + @Override + public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable e, Response response) { + if (e instanceof EOFException) { + return; // ignore + } + this.socket.getPlugin().getLogger().warn("Exception occurred in web socket", e); + } + + @Override + public void onMessage(@NonNull WebSocket webSocket, @NonNull String msg) { + this.socket.getPlugin().getBootstrap().getScheduler().executeAsync(() -> { + this.lock.lock(); + try { + if (shouldIgnoreMessages()) { + return; + } + + handleMessageFrame(msg); + } catch (Exception e) { + this.socket.getPlugin().getLogger().warn("Exception occurred handling message from socket", e); + } finally { + this.lock.unlock(); + } + }); + } + + /** + * Checks if incoming messages should be ignored. + * + * @return true if messages should be ignored + */ + public boolean shouldIgnoreMessages() { + if (this.socket.isClosed()) { + return true; + } + + if (!this.socket.getSender().isValid()) { + this.socket.close(); + return true; + } + + return false; + } + + private void handleMessageFrame(String stringMsg) { + JsonObject frame = GsonProvider.parser().parse(stringMsg).getAsJsonObject(); + + String innerMsg = frame.get("msg").getAsString(); + String signature = frame.get("signature").getAsString(); + + if (innerMsg == null || innerMsg.isEmpty() || signature == null || signature.isEmpty()) { + throw new IllegalArgumentException("Incomplete message"); + } + + // check signature to ensure the message is from the connected editor + PublicKey remotePublicKey = this.socket.getRemotePublicKey(); + boolean verified = remotePublicKey == null || CryptographyUtils.verify(remotePublicKey, innerMsg, signature); + + // parse the inner message + JsonObject msg = GsonProvider.parser().parse(innerMsg).getAsJsonObject(); + SocketMessageType type = SocketMessageType.getById(msg.get("type").getAsString()); + + if (type == SocketMessageType.HELLO) { + this.helloHandler.handle(msg); + return; + } + + if (!verified) { + throw new IllegalStateException("Signature not accepted"); + } + + switch (type) { + case CHANGE_REQUEST: + this.changeRequestHandler.handle(msg); + break; + case CONNECTED: + this.connectedHandler.handle(msg); + break; + case PING: + this.pingHandler.handle(msg); + break; + default: + throw new IllegalStateException("Invalid message type: " + type); + } + } + + public CompletableFuture connectFuture() { + return this.connectFuture; + } + + public HandlerHello helloHandler() { + return this.helloHandler; + } + + public HandlerConnected connectedHandler() { + return this.connectedHandler; + } + + public HandlerPing pingHandler() { + return this.pingHandler; + } + + public HandlerChangeRequest changeRequestHandler() { + return this.changeRequestHandler; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/RemoteSession.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/RemoteSession.java new file mode 100644 index 000000000..1759df8cc --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/RemoteSession.java @@ -0,0 +1,51 @@ +/* + * 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.webeditor.store; + +import me.lucko.luckperms.common.webeditor.WebEditorRequest; + +public final class RemoteSession { + private WebEditorRequest request; + private boolean completed; + + public RemoteSession(WebEditorRequest request) { + this.request = request; + this.completed = false; + } + + public WebEditorRequest request() { + return this.request; + } + + public boolean isCompleted() { + return this.completed; + } + + public void complete() { + this.completed = true; + this.request = null; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorKeystore.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorKeystore.java new file mode 100644 index 000000000..ab64c4ae6 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorKeystore.java @@ -0,0 +1,196 @@ +/* + * 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.webeditor.store; + +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.node.types.Meta; +import me.lucko.luckperms.common.query.QueryOptionsImpl; +import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.gson.GsonProvider; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; + +import net.luckperms.api.model.data.DataType; +import net.luckperms.api.node.NodeType; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +public final class WebEditorKeystore { + private static final String META_KEY = "lp-editor-key"; + + private final Path consoleKeysPath; + private final Set trustedConsoleKeys; + + public WebEditorKeystore(Path consoleKeysPath) { + this.consoleKeysPath = consoleKeysPath; + this.trustedConsoleKeys = new CopyOnWriteArraySet<>(); + + try { + load(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Checks if the given public key has been trusted by the sender. + * + * @param sender the sender + * @param publicKey the public key + * @return true if trusted + */ + public boolean isTrusted(Sender sender, byte[] publicKey) { + return isTrusted(sender, hash(publicKey)); + } + + /** + * Checks if the given public key hash has been trusted by the sender. + * + * @param sender the sender + * @param hash the public key hash + * @return true if trusted + */ + public boolean isTrusted(Sender sender, String hash) { + if (sender.isConsole()) { + return isTrustedConsole(hash); + } else { + User user = sender.getPlugin().getUserManager().getIfLoaded(sender.getUniqueId()); + return user != null && isTrusted(user, hash); + } + } + + /** + * Trusts the given public key for the sender. + * + * @param sender the sender + * @param publicKey the public key + */ + public void trust(Sender sender, byte[] publicKey) { + trust(sender, hash(publicKey)); + } + + /** + * Trusts the given public key hash for the sender. + * + * @param sender the sender + * @param hash the public key hash + */ + public void trust(Sender sender, String hash) { + if (sender.isConsole()) { + trustConsole(hash); + } else { + User user = sender.getPlugin().getUserManager().getIfLoaded(sender.getUniqueId()); + if (user != null) { + trust(user, hash); + } + } + } + + // console + + private boolean isTrustedConsole(String hash) { + return this.trustedConsoleKeys.contains(hash); + } + + private void trustConsole(String hash) { + this.trustedConsoleKeys.add(hash); + + try { + save(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void load() throws Exception { + if (Files.exists(this.consoleKeysPath)) { + try (BufferedReader reader = Files.newBufferedReader(this.consoleKeysPath, StandardCharsets.UTF_8)) { + KeystoreFile file = GsonProvider.normal().fromJson(reader, KeystoreFile.class); + if (file != null && file.consoleKeys != null) { + this.trustedConsoleKeys.addAll(file.consoleKeys); + } + } + } + } + + private void save() throws Exception { + try (BufferedWriter writer = Files.newBufferedWriter(this.consoleKeysPath, StandardCharsets.UTF_8)) { + KeystoreFile file = new KeystoreFile(); + file.consoleKeys = new ArrayList<>(this.trustedConsoleKeys); + GsonProvider.prettyPrinting().toJson(file, writer); + } + } + + // users + + private boolean isTrusted(User user, String hash) { + String key = user.getCachedData().getMetaData(QueryOptionsImpl.DEFAULT_CONTEXTUAL) + .getMetaValue(META_KEY, CheckOrigin.INTERNAL).result(); + + if (key == null || key.isEmpty()) { + return false; + } + + return hash.equals(key); + } + + private void trust(User user, String hash) { + user.removeIf(DataType.NORMAL, ImmutableContextSetImpl.EMPTY, NodeType.META.predicate(mn -> mn.getMetaKey().equals(META_KEY)), false); + user.setNode(DataType.NORMAL, Meta.builder(META_KEY, hash).build(), false); + + user.getPlugin().getStorage().saveUser(user).join(); + } + + private static String hash(byte[] buf) { + byte[] digest = createDigest().digest(buf); + return Base64.getEncoder().encodeToString(digest); + } + + private static MessageDigest createDigest() { + try { + return MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings({"FieldMayBeFinal", "unused"}) + private static class KeystoreFile { + private String _comment = "This file stores a list of trusted editor public keys"; + private List consoleKeys = null; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSessionMap.java similarity index 64% rename from common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java rename to common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSessionMap.java index be7d897f8..473983b42 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSessionStore.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSessionMap.java @@ -23,45 +23,35 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.webeditor; +package me.lucko.luckperms.common.webeditor.store; -import org.checkerframework.checker.nullness.qual.NonNull; +import me.lucko.luckperms.common.webeditor.WebEditorRequest; + +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -/** - * Contains a store of known web editor sessions. - */ -public class WebEditorSessionStore { - private final Map sessions = new ConcurrentHashMap<>(); +public final class WebEditorSessionMap { + private final Map sessions = new ConcurrentHashMap<>(); /** * Adds a newly created session to the store. * * @param id the id of the session */ - public void addNewSession(String id) { - this.sessions.put(id, SessionState.IN_PROGRESS); - } - - /** - * Gets the session state for the given session id. - * - * @param id the id of the session - * @return the session state - */ - public @NonNull SessionState getSessionState(String id) { - return this.sessions.getOrDefault(id, SessionState.NOT_KNOWN); + public void addNewSession(String id, WebEditorRequest request) { + this.sessions.put(id, new RemoteSession(request)); } /** - * Marks a given session as complete. + * Gets the session for the given session id. * * @param id the id of the session + * @return the session */ - public void markSessionCompleted(String id) { - this.sessions.put(id, SessionState.COMPLETED); + public @Nullable RemoteSession getSession(String id) { + return this.sessions.get(id); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSocketMap.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSocketMap.java new file mode 100644 index 000000000..9e7fb2de7 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSocketMap.java @@ -0,0 +1,50 @@ +/* + * 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.webeditor.store; + +import com.github.benmanes.caffeine.cache.Cache; + +import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.CaffeineFactory; +import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public final class WebEditorSocketMap { + private final Cache sockets = CaffeineFactory.newBuilder() + .weakValues() + .expireAfterWrite(5, TimeUnit.MINUTES) + .build(); + + public WebEditorSocket getSocket(Sender sender) { + return this.sockets.getIfPresent(sender.getUniqueId()); + } + + public void putSocket(Sender sender, WebEditorSocket socket) { + this.sockets.put(sender.getUniqueId(), socket); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java new file mode 100644 index 000000000..82a7bd47a --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java @@ -0,0 +1,57 @@ +/* + * 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.webeditor.store; + +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +/** + * Contains a store of known web editor sessions and provides a lookup function for + * trusted editor public keys. + */ +public class WebEditorStore { + private final WebEditorSessionMap sessions; + private final WebEditorSocketMap sockets; + private final WebEditorKeystore keystore; + + public WebEditorStore(LuckPermsPlugin plugin) { + this.sessions = new WebEditorSessionMap(); + this.sockets = new WebEditorSocketMap(); + this.keystore = new WebEditorKeystore(plugin.getBootstrap().getConfigDirectory().resolve("editor-keystore.json")); + } + + public WebEditorSessionMap sessions() { + return this.sessions; + } + + public WebEditorSocketMap sockets() { + return this.sockets; + } + + public WebEditorKeystore keystore() { + return this.keystore; + } + +} diff --git a/common/src/main/resources/luckperms_en.properties b/common/src/main/resources/luckperms_en.properties index 220e48017..6ce54db35 100644 --- a/common/src/main/resources/luckperms_en.properties +++ b/common/src/main/resources/luckperms_en.properties @@ -91,6 +91,18 @@ luckperms.command.misc.loading.error.track-invalid={0} is not a valid track name luckperms.command.editor.no-match=Unable to open editor, no objects matched the desired type luckperms.command.editor.start=Preparing a new editor session, please wait... luckperms.command.editor.url=Click the link below to open the editor +luckperms.command.editor.socket.connected=Editor window connected successfully +luckperms.command.editor.socket.reconnected=Editor window reconnected successfully +luckperms.command.editor.socket.changes-received=Changes have been received from the connected web editor session +luckperms.command.editor.socket.untrusted=An editor window has connected, but it is not yet trusted +luckperms.command.editor.socket.untrusted.prompt.click=If it was you, {0} to trust the session! +luckperms.command.editor.socket.untrusted.prompt.click.action=click here +luckperms.command.editor.socket.untrusted.prompt.runcommand=If it was you, run {0} to trust the session! +luckperms.command.editor.socket.untrusted.sessioninfo=session id = {0}, browser = {1} +luckperms.command.editor.socket.trust.success=The editor session has been marked as trusted +luckperms.command.editor.socket.trust.futureinfo=In the future, connections from the same browser will be trusted automatically +luckperms.command.editor.socket.trust.connecting=The plugin will now attempt to establish a connection with the editor... +luckperms.command.editor.socket.trust.failure=Unable to trust the given session because the socket is closed, or because a different connection was established instead luckperms.command.editor.unable-to-communicate=Unable to communicate with the editor luckperms.command.editor.apply-edits.success=Web editor data was applied to {0} {1} successfully luckperms.command.editor.apply-edits.success-summary={0} {1} and {2} {3} diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricCommandListUpdater.java b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricCommandListUpdater.java index 7bd512e1e..3fb1c6203 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricCommandListUpdater.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricCommandListUpdater.java @@ -27,16 +27,16 @@ import com.github.benmanes.caffeine.cache.LoadingCache; -import me.lucko.luckperms.fabric.LPFabricPlugin; +import me.lucko.luckperms.common.api.implementation.ApiGroup; import me.lucko.luckperms.common.cache.BufferedRequest; import me.lucko.luckperms.common.event.LuckPermsEventListener; import me.lucko.luckperms.common.util.CaffeineFactory; -import me.lucko.luckperms.common.api.implementation.ApiGroup; +import me.lucko.luckperms.fabric.LPFabricPlugin; import net.luckperms.api.event.EventBus; import net.luckperms.api.event.context.ContextUpdateEvent; -import net.luckperms.api.event.user.UserDataRecalculateEvent; import net.luckperms.api.event.group.GroupDataRecalculateEvent; +import net.luckperms.api.event.user.UserDataRecalculateEvent; import net.minecraft.server.network.ServerPlayerEntity; import java.util.UUID; diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/CommandManagerMixin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/CommandManagerMixin.java index 4ff389685..a08a30fcf 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/CommandManagerMixin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/CommandManagerMixin.java @@ -29,6 +29,7 @@ import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; From ee79f53612addc11cc080492ced82a7561b6477c Mon Sep 17 00:00:00 2001 From: lucko Date: Mon, 7 Feb 2022 21:10:01 +0000 Subject: [PATCH 061/314] SpongeAPI 8 (#2728) --- .../bungee/context/BungeeContextManager.java | 18 +- .../luckperms/common/backup/Exporter.java | 3 +- .../luckperms/common/backup/Importer.java | 3 +- .../cacheddata/AbstractCachedDataManager.java | 3 +- .../context/manager/ContextManager.java | 28 ++- .../manager/InlineQueryOptionsSupplier.java | 45 ++++ .../manager/user/AbstractUserManager.java | 7 +- .../ReflectionClassPathAppender.java | 7 +- .../common/sender/AbstractSender.java | 2 +- .../common/util/CompletableFutures.java | 37 ++- settings.gradle | 2 +- sponge/build.gradle | 23 +- .../proxy/api6/DescriptionBuilder.java | 142 ------------ .../api6/PermissionDescriptionProxy.java | 89 -------- .../proxy/api6/PermissionServiceProxy.java | 121 ---------- .../proxy/api6/SubjectCollectionProxy.java | 128 ----------- .../service/proxy/api6/SubjectDataProxy.java | 211 ------------------ .../service/proxy/api6/SubjectProxy.java | 187 ---------------- sponge/sponge-service-api7/build.gradle | 15 -- .../build.gradle | 4 +- .../proxy/api8}/DescriptionBuilder.java | 35 ++- .../api8}/PermissionDescriptionProxy.java | 60 ++++- .../proxy/api8}/PermissionServiceProxy.java | 61 ++--- .../proxy/api8}/SubjectCollectionProxy.java | 39 ++-- .../service/proxy/api8}/SubjectDataProxy.java | 100 +++++++-- .../service/proxy/api8}/SubjectProxy.java | 95 ++++---- sponge/sponge-service/build.gradle | 4 +- .../service/PermissionAndContextService.java} | 13 +- .../model/LPPermissionDescription.java | 7 +- .../service/model/LPPermissionService.java | 24 +- ...bject.java => LPProxiedServiceObject.java} | 2 +- ...xiedSubject.java => LPProxiedSubject.java} | 2 +- .../sponge/service/model/LPSubject.java | 7 +- .../service/model/LPSubjectCollection.java | 3 +- .../sponge/service/model/LPSubjectUser.java} | 18 +- .../reference/CachedSubjectReference.java | 8 +- .../reference/SubjectReferenceFactory.java | 24 +- .../luckperms/sponge/LPSpongeBootstrap.java | 140 +++++------- .../luckperms/sponge/LPSpongePlugin.java | 151 +++++++++---- .../sponge/SpongeClassPathAppender.java | 64 ++++++ .../sponge/SpongeCommandExecutor.java | 53 ++--- .../luckperms/sponge/SpongeEventBus.java | 8 +- .../sponge/SpongeSchedulerAdapter.java | 78 ++++--- .../luckperms/sponge/SpongeSenderFactory.java | 71 +++--- .../luckperms/sponge/commands/ParentAdd.java | 4 +- .../sponge/commands/ParentRemove.java | 4 +- .../sponge/commands/SpongeCommandUtils.java | 4 +- .../sponge/context/SpongeContextManager.java | 69 ++++-- .../context/SpongePlayerCalculator.java | 108 ++++----- .../listeners/SpongeCommandListUpdater.java | 108 +++++++++ .../listeners/SpongeConnectionListener.java | 69 +++--- .../listeners/SpongePlatformListener.java | 15 +- .../messaging/PluginMessageMessenger.java | 62 ++--- .../model/manager/SpongeGroupManager.java | 3 +- .../model/manager/SpongeUserManager.java | 3 +- .../sponge/service/LuckPermsService.java | 43 +++- .../sponge/service/ProxyFactory.java | 49 ++-- .../service/model/ContextCalculatorProxy.java | 59 +++-- .../model/SimplePermissionDescription.java | 11 +- .../SubjectDataUpdateEventImpl.java} | 17 +- .../model/TemporaryCauseHolderSubject.java | 108 +++++++++ .../model/permissionholder/GroupSubject.java | 7 - .../PermissionHolderSubject.java | 15 +- .../PermissionHolderSubjectData.java | 8 +- .../model/permissionholder/UserSubject.java | 16 +- .../model/persisted/PersistedCollection.java | 3 +- .../model/persisted/PersistedSubject.java | 29 +-- .../model/persisted/SubjectDataContainer.java | 4 +- .../resources/META-INF/sponge_plugins.json | 31 +++ sponge/src/main/resources/luckperms.conf | 3 + 70 files changed, 1294 insertions(+), 1700 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/context/manager/InlineQueryOptionsSupplier.java rename sponge/src/main/java/me/lucko/luckperms/sponge/service/events/UpdateEventHandler.java => common/src/main/java/me/lucko/luckperms/common/util/CompletableFutures.java (52%) delete mode 100644 sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/DescriptionBuilder.java delete mode 100644 sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/PermissionDescriptionProxy.java delete mode 100644 sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/PermissionServiceProxy.java delete mode 100644 sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectCollectionProxy.java delete mode 100644 sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectDataProxy.java delete mode 100644 sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java delete mode 100644 sponge/sponge-service-api7/build.gradle rename sponge/{sponge-service-api6 => sponge-service-api8}/build.gradle (70%) rename sponge/{sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7 => sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8}/DescriptionBuilder.java (81%) rename sponge/{sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7 => sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8}/PermissionDescriptionProxy.java (65%) rename sponge/{sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7 => sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8}/PermissionServiceProxy.java (72%) rename sponge/{sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7 => sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8}/SubjectCollectionProxy.java (79%) rename sponge/{sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7 => sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8}/SubjectDataProxy.java (68%) rename sponge/{sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7 => sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8}/SubjectProxy.java (55%) rename sponge/{sponge-service-api7/src/main/java/org/spongepowered/api/event/permission/SubjectDataUpdateEvent.java => sponge-service/src/main/java/me/lucko/luckperms/sponge/service/PermissionAndContextService.java} (76%) rename sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/{ProxiedServiceObject.java => LPProxiedServiceObject.java} (97%) rename sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/{ProxiedSubject.java => LPProxiedSubject.java} (95%) rename sponge/{sponge-service-api6/src/main/java/org/spongepowered/api/service/permission/SubjectReference.java => sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectUser.java} (76%) create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/SpongeClassPathAppender.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeCommandListUpdater.java rename sponge/src/main/java/me/lucko/luckperms/sponge/service/{events/LPSubjectDataUpdateEvent.java => model/SubjectDataUpdateEventImpl.java} (81%) create mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/service/model/TemporaryCauseHolderSubject.java create mode 100644 sponge/src/main/resources/META-INF/sponge_plugins.json 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 73bf1112d..364ce3182 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 @@ -29,6 +29,7 @@ import me.lucko.luckperms.bungee.LPBungeePlugin; import me.lucko.luckperms.common.context.manager.ContextManager; +import me.lucko.luckperms.common.context.manager.InlineQueryOptionsSupplier; import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.common.util.CaffeineFactory; @@ -60,9 +61,10 @@ public QueryOptionsSupplier getCacheFor(ProxiedPlayer subject) { throw new NullPointerException("subject"); } - return new InlineQueryOptionsSupplier(subject, this.contextsCache); + return new InlineQueryOptionsSupplier<>(subject, this.contextsCache); } + // override getContext, getQueryOptions and invalidateCache to skip the QueryOptionsSupplier @Override public ImmutableContextSet getContext(ProxiedPlayer subject) { return getQueryOptions(subject).context(); @@ -83,18 +85,4 @@ public QueryOptions formQueryOptions(ProxiedPlayer subject, ImmutableContextSet return formQueryOptions(contextSet); } - private static final class InlineQueryOptionsSupplier implements QueryOptionsSupplier { - private final ProxiedPlayer key; - private final LoadingCache cache; - - private InlineQueryOptionsSupplier(ProxiedPlayer key, LoadingCache cache) { - this.key = key; - this.cache = cache; - } - - @Override - public QueryOptions getQueryOptions() { - return this.cache.get(this.key); - } - } } diff --git a/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java b/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java index b326b7aa9..08b029bd2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java +++ b/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java @@ -38,6 +38,7 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.storage.Storage; +import me.lucko.luckperms.common.util.CompletableFutures; import me.lucko.luckperms.common.util.gson.GsonProvider; import me.lucko.luckperms.common.util.gson.JArray; import me.lucko.luckperms.common.util.gson.JObject; @@ -193,7 +194,7 @@ private JsonObject exportUsers() { } // all of the threads have been scheduled now and are running. we just need to wait for them all to complete - CompletableFuture overallFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + CompletableFuture overallFuture = CompletableFutures.allOf(futures); while (true) { try { diff --git a/common/src/main/java/me/lucko/luckperms/common/backup/Importer.java b/common/src/main/java/me/lucko/luckperms/common/backup/Importer.java index 26bfdd4d9..820604389 100644 --- a/common/src/main/java/me/lucko/luckperms/common/backup/Importer.java +++ b/common/src/main/java/me/lucko/luckperms/common/backup/Importer.java @@ -39,6 +39,7 @@ import me.lucko.luckperms.common.node.utils.NodeJsonSerializer; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.CompletableFutures; import me.lucko.luckperms.common.util.Uuids; import net.luckperms.api.event.cause.CreationCause; @@ -260,7 +261,7 @@ public void run() { } // all of the threads have been scheduled now and are running. we just need to wait for them all to complete - CompletableFuture overallFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + CompletableFuture overallFuture = CompletableFutures.allOf(futures); this.notify.forEach(s -> Message.IMPORT_INFO.send(s, "All data entries have been processed and scheduled for import - now waiting for the execution to complete.")); diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java index 930db77fe..b48a450bd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java @@ -33,6 +33,7 @@ import me.lucko.luckperms.common.calculator.PermissionCalculator; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.util.CaffeineFactory; +import me.lucko.luckperms.common.util.CompletableFutures; import net.luckperms.api.cacheddata.CachedData; import net.luckperms.api.cacheddata.CachedDataManager; @@ -241,7 +242,7 @@ public void recalculate() { @Override public @NonNull CompletableFuture reload() { Set keys = this.cache.keySet(); - return CompletableFuture.allOf(keys.stream().map(this::reload).toArray(CompletableFuture[]::new)); + return CompletableFutures.allOf(keys.stream().map(this::reload)); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java b/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java index 4c82dcdd8..dbdfddfe1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/context/manager/ContextManager.java @@ -135,16 +135,28 @@ public void unregisterCalculator(ContextCalculator calculator) { this.calculators.remove(calculator); } + protected void callContextCalculator(ContextCalculator calculator, S subject, ContextConsumer consumer) { + try { + calculator.calculate(subject, consumer); + } catch (Throwable e) { + this.plugin.getLogger().warn("An exception was thrown by " + getCalculatorClass(calculator) + " whilst calculating the context of subject " + subject, e); + } + } + + protected void callStaticContextCalculator(StaticContextCalculator calculator, ContextConsumer consumer) { + try { + calculator.calculate(consumer); + } catch (Throwable e) { + this.plugin.getLogger().warn("An exception was thrown by " + getCalculatorClass(calculator) + " whilst calculating static contexts", e); + } + } + protected QueryOptions calculate(S subject) { ImmutableContextSet.Builder accumulator = new ImmutableContextSetImpl.BuilderImpl(); ContextConsumer consumer = accumulator::add; for (ContextCalculator calculator : this.calculators.calculators()) { - try { - calculator.calculate(subject, consumer); - } catch (Throwable e) { - this.plugin.getLogger().warn("An exception was thrown by " + getCalculatorClass(calculator) + " whilst calculating the context of subject " + subject, e); - } + callContextCalculator(calculator, subject, consumer); } return formQueryOptions(subject, accumulator.build()); @@ -155,11 +167,7 @@ private QueryOptions calculateStatic() { ContextConsumer consumer = accumulator::add; for (StaticContextCalculator calculator : this.calculators.staticCalculators()) { - try { - calculator.calculate(consumer); - } catch (Throwable e) { - this.plugin.getLogger().warn("An exception was thrown by " + getCalculatorClass(calculator) + " whilst calculating static contexts", e); - } + callStaticContextCalculator(calculator, consumer); } return formQueryOptions(accumulator.build()); diff --git a/common/src/main/java/me/lucko/luckperms/common/context/manager/InlineQueryOptionsSupplier.java b/common/src/main/java/me/lucko/luckperms/common/context/manager/InlineQueryOptionsSupplier.java new file mode 100644 index 000000000..798a1d772 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/context/manager/InlineQueryOptionsSupplier.java @@ -0,0 +1,45 @@ +/* + * 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.context.manager; + +import com.github.benmanes.caffeine.cache.LoadingCache; + +import net.luckperms.api.query.QueryOptions; + +public final class InlineQueryOptionsSupplier implements QueryOptionsSupplier { + private final T key; + private final LoadingCache cache; + + public InlineQueryOptionsSupplier(T key, LoadingCache cache) { + this.key = key; + this.cache = cache; + } + + @Override + public QueryOptions getQueryOptions() { + return this.cache.get(this.key); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java b/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java index 451fe11bb..bd3b4771e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/manager/user/AbstractUserManager.java @@ -32,6 +32,7 @@ import me.lucko.luckperms.common.model.manager.group.GroupManager; import me.lucko.luckperms.common.node.types.Inheritance; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.util.CompletableFutures; import me.lucko.luckperms.common.verbose.event.CheckOrigin; import net.luckperms.api.model.data.DataType; @@ -165,11 +166,9 @@ public CompletableFuture loadAllUsers() { Set ids = new HashSet<>(getAll().keySet()); ids.addAll(this.plugin.getBootstrap().getOnlinePlayers()); - CompletableFuture[] loadTasks = ids.stream() + return ids.stream() .map(id -> this.plugin.getStorage().loadUser(id, null)) - .toArray(CompletableFuture[]::new); - - return CompletableFuture.allOf(loadTasks); + .collect(CompletableFutures.collector()); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java index 11cdb8f97..c33aafb96 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/classpath/ReflectionClassPathAppender.java @@ -34,8 +34,7 @@ public class ReflectionClassPathAppender implements ClassPathAppender { private final URLClassLoaderAccess classLoaderAccess; - public ReflectionClassPathAppender(LuckPermsBootstrap bootstrap) throws IllegalStateException { - ClassLoader classLoader = bootstrap.getClass().getClassLoader(); + public ReflectionClassPathAppender(ClassLoader classLoader) throws IllegalStateException { if (classLoader instanceof URLClassLoader) { this.classLoaderAccess = URLClassLoaderAccess.create((URLClassLoader) classLoader); } else { @@ -43,6 +42,10 @@ public ReflectionClassPathAppender(LuckPermsBootstrap bootstrap) throws IllegalS } } + public ReflectionClassPathAppender(LuckPermsBootstrap bootstrap) throws IllegalStateException { + this(bootstrap.getClass().getClassLoader()); + } + @Override public void addJarToClasspath(Path file) { try { diff --git a/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java b/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java index 63e8601c6..1722722eb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java +++ b/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java @@ -130,7 +130,7 @@ public int hashCode() { // A small utility method which splits components built using // > join(newLine(), components...) // back into separate components. - private static Iterable splitNewlines(Component message) { + public static Iterable splitNewlines(Component message) { if (message instanceof TextComponent && message.style().isEmpty() && !message.children().isEmpty() && ((TextComponent) message).content().isEmpty()) { LinkedList> split = new LinkedList<>(); split.add(new ArrayList<>()); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/events/UpdateEventHandler.java b/common/src/main/java/me/lucko/luckperms/common/util/CompletableFutures.java similarity index 52% rename from sponge/src/main/java/me/lucko/luckperms/sponge/service/events/UpdateEventHandler.java rename to common/src/main/java/me/lucko/luckperms/common/util/CompletableFutures.java index a2fc9e4b4..93244f82f 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/events/UpdateEventHandler.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/CompletableFutures.java @@ -23,20 +23,35 @@ * SOFTWARE. */ -package me.lucko.luckperms.sponge.service.events; +package me.lucko.luckperms.common.util; -import me.lucko.luckperms.sponge.LPSpongePlugin; -import me.lucko.luckperms.sponge.service.model.LPSubjectData; +import com.google.common.collect.ImmutableList; -import org.spongepowered.api.event.permission.SubjectDataUpdateEvent; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collector; +import java.util.stream.Stream; -public final class UpdateEventHandler { - private UpdateEventHandler() {} +public final class CompletableFutures { + private CompletableFutures() {} - public static void fireUpdateEvent(LPSpongePlugin plugin, LPSubjectData subjectData) { - plugin.getBootstrap().getScheduler().executeAsync(() -> { - SubjectDataUpdateEvent event = new LPSubjectDataUpdateEvent(plugin, subjectData); - plugin.getBootstrap().getGame().getEventManager().post(event); - }); + public static > Collector, CompletableFuture> collector() { + return Collector.of( + ImmutableList.Builder::new, + ImmutableList.Builder::add, + (l, r) -> l.addAll(r.build()), + builder -> allOf(builder.build()) + ); } + + public static CompletableFuture allOf(Stream> futures) { + CompletableFuture[] arr = futures.toArray(CompletableFuture[]::new); + return CompletableFuture.allOf(arr); + } + + public static CompletableFuture allOf(Collection> futures) { + CompletableFuture[] arr = futures.toArray(new CompletableFuture[0]); + return CompletableFuture.allOf(arr); + } + } diff --git a/settings.gradle b/settings.gradle index 37f938678..db3bdea62 100644 --- a/settings.gradle +++ b/settings.gradle @@ -23,6 +23,6 @@ include ( 'fabric', 'nukkit', 'nukkit:loader', - 'sponge', 'sponge:sponge-service', 'sponge:sponge-service-api6', 'sponge:sponge-service-api7', + 'sponge', 'sponge:sponge-service', 'sponge:sponge-service-api8', 'velocity' ) diff --git a/sponge/build.gradle b/sponge/build.gradle index 67038ca9f..275da4a33 100644 --- a/sponge/build.gradle +++ b/sponge/build.gradle @@ -1,25 +1,17 @@ plugins { - id 'net.kyori.blossom' version '1.3.0' id 'com.github.johnrengelman.shadow' version '7.0.0' } repositories { - maven { url 'https://repo.spongepowered.org/maven' } + maven { url 'https://repo.spongepowered.org/repository/maven-public/' } } dependencies { implementation project(':common') implementation project(':sponge:sponge-service') - implementation project(':sponge:sponge-service-api6') - implementation project(':sponge:sponge-service-api7') + implementation project(':sponge:sponge-service-api8') - compileOnly('org.spongepowered:spongeapi:7.3.0') { - exclude(module: 'configurate-core') - exclude(module: 'configurate-hocon') - exclude(module: 'configurate-gson') - exclude(module: 'configurate-yaml') - } - annotationProcessor('org.spongepowered:spongeapi:7.3.0') { + compileOnly('org.spongepowered:spongeapi:8.0.0') { exclude(module: 'configurate-core') exclude(module: 'configurate-hocon') exclude(module: 'configurate-gson') @@ -27,9 +19,10 @@ dependencies { } } -blossom { - replaceTokenIn('src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java') - replaceToken '@version@', project.ext.fullVersion +processResources { + filesMatching('META-INF/sponge_plugins.json') { + expand 'pluginVersion': project.ext.fullVersion + } } shadowJar { @@ -40,7 +33,7 @@ shadowJar { include(dependency('me.lucko.luckperms:.*')) } - relocate 'net.kyori.adventure', 'me.lucko.luckperms.lib.adventure' + //relocate 'net.kyori.adventure', 'me.lucko.luckperms.lib.adventure' relocate 'net.kyori.event', 'me.lucko.luckperms.lib.eventbus' relocate 'com.github.benmanes.caffeine', 'me.lucko.luckperms.lib.caffeine' relocate 'okio', 'me.lucko.luckperms.lib.okio' diff --git a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/DescriptionBuilder.java b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/DescriptionBuilder.java deleted file mode 100644 index f6d3bd583..000000000 --- a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/DescriptionBuilder.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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.sponge.service.proxy.api6; - -import me.lucko.luckperms.common.context.ImmutableContextSetImpl; -import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; -import me.lucko.luckperms.sponge.service.model.LPPermissionService; -import me.lucko.luckperms.sponge.service.model.LPSubject; -import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; - -import net.luckperms.api.util.Tristate; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.plugin.PluginContainer; -import org.spongepowered.api.service.permission.PermissionDescription; -import org.spongepowered.api.service.permission.PermissionService; -import org.spongepowered.api.text.Text; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public final class DescriptionBuilder implements PermissionDescription.Builder, ProxiedServiceObject { - - public static LPPermissionDescription registerDescription(LPPermissionService service, PermissionDescription description) { - //noinspection ConstantConditions - if (description.getOwner() == null) { - return null; - } - return service.registerPermissionDescription(description.getId(), description.getDescription(), description.getOwner()); - } - - private final @NonNull LPPermissionService service; - private final @NonNull PluginContainer container; - private final @NonNull Map roles = new HashMap<>(); - private @Nullable String id = null; - private @Nullable Text description = null; - - public DescriptionBuilder(@NonNull LPPermissionService service, @NonNull PluginContainer container) { - this.service = Objects.requireNonNull(service, "service"); - this.container = Objects.requireNonNull(container, "container"); - } - - @Override - public PermissionDescription.@NonNull Builder id(@NonNull String id) { - this.id = Objects.requireNonNull(id, "id"); - return this; - } - - @Override - public PermissionDescription.@NonNull Builder description(@NonNull Text description) { - this.description = Objects.requireNonNull(description, "description"); - return this; - } - - @Override - public PermissionDescription.@NonNull Builder assign(@NonNull String role, boolean value) { - Objects.requireNonNull(role, "role"); - this.roles.put(role, Tristate.of(value)); - return this; - } - - @Override - public @NonNull PermissionDescription register() throws IllegalStateException { - if (this.id == null) { - throw new IllegalStateException("id cannot be null"); - } - - LPPermissionDescription description = this.service.registerPermissionDescription(this.id, this.description, this.container); - - // Set role-templates - LPSubjectCollection subjects = this.service.getCollection(PermissionService.SUBJECTS_ROLE_TEMPLATE); - for (Map.Entry assignment : this.roles.entrySet()) { - LPSubject roleSubject = subjects.loadSubject(assignment.getKey()).join(); - roleSubject.getTransientSubjectData().setPermission(ImmutableContextSetImpl.EMPTY, this.id, assignment.getValue()); - } - - // null stuff so this instance can be reused - this.roles.clear(); - this.id = null; - this.description = null; - - return description.sponge(); - } - - @Override - public boolean equals(Object o) { - if (o == this) return true; - if (!(o instanceof DescriptionBuilder)) return false; - final DescriptionBuilder other = (DescriptionBuilder) o; - - return this.container.equals(other.container) && - this.roles.equals(other.roles) && - Objects.equals(this.id, other.id) && - Objects.equals(this.description, other.description); - } - - @Override - public int hashCode() { - final int PRIME = 59; - int result = 1; - result = result * PRIME + this.container.hashCode(); - result = result * PRIME + this.roles.hashCode(); - result = result * PRIME + (this.id == null ? 43 : this.id.hashCode()); - result = result * PRIME + (this.description == null ? 43 : this.description.hashCode()); - return result; - } - - @Override - public String toString() { - return "SimpleDescriptionBuilder(" + - "container=" + this.container + ", " + - "roles=" + this.roles + ", " + - "id=" + this.id + ", " + - "description=" + this.description + ")"; - } -} diff --git a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/PermissionDescriptionProxy.java b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/PermissionDescriptionProxy.java deleted file mode 100644 index e59f41c7d..000000000 --- a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/PermissionDescriptionProxy.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.sponge.service.proxy.api6; - -import me.lucko.luckperms.common.util.ImmutableCollectors; -import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; -import me.lucko.luckperms.sponge.service.model.LPPermissionService; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.Sponge; -import org.spongepowered.api.plugin.PluginContainer; -import org.spongepowered.api.service.permission.PermissionDescription; -import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.text.Text; - -import java.util.Map; - -public final class PermissionDescriptionProxy implements PermissionDescription, ProxiedServiceObject { - private final LPPermissionService service; - private final LPPermissionDescription handle; - - public PermissionDescriptionProxy(LPPermissionService service, LPPermissionDescription handle) { - this.service = service; - this.handle = handle; - } - - @Override - public @NonNull String getId() { - return this.handle.getId(); - } - - @Override - public @NonNull Text getDescription() { - return this.handle.getDescription().orElse(Text.EMPTY); - } - - @Override - public @NonNull PluginContainer getOwner() { - return this.handle.getOwner().orElseGet(() -> Sponge.getGame().getPluginManager().fromInstance(this.service.getPlugin()).orElseThrow(() -> new RuntimeException("Unable to get LuckPerms instance."))); - } - - @Override - public @NonNull Map getAssignedSubjects(@NonNull String s) { - return this.handle.getAssignedSubjects(s).entrySet().stream() - .collect(ImmutableCollectors.toMap( - e -> new SubjectProxy(this.service, e.getKey().toReference()), - Map.Entry::getValue - )); - } - - @Override - public boolean equals(Object o) { - return o == this || o instanceof PermissionDescriptionProxy && this.handle.equals(((PermissionDescriptionProxy) o).handle); - } - - @Override - public int hashCode() { - return this.handle.hashCode(); - } - - @Override - public String toString() { - return "luckperms.api6.PermissionDescriptionProxy(handle=" + this.handle + ")"; - } -} diff --git a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/PermissionServiceProxy.java b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/PermissionServiceProxy.java deleted file mode 100644 index 18e278ec8..000000000 --- a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/PermissionServiceProxy.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.sponge.service.proxy.api6; - -import me.lucko.luckperms.common.util.ImmutableCollectors; -import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; -import me.lucko.luckperms.sponge.service.model.LPPermissionService; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.Sponge; -import org.spongepowered.api.plugin.PluginContainer; -import org.spongepowered.api.service.context.ContextCalculator; -import org.spongepowered.api.service.permission.PermissionDescription; -import org.spongepowered.api.service.permission.PermissionService; -import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.service.permission.SubjectCollection; - -import java.util.Collection; -import java.util.Map; -import java.util.Optional; - -public final class PermissionServiceProxy implements PermissionService, ProxiedServiceObject { - private final LPPermissionService handle; - - public PermissionServiceProxy(LPPermissionService handle) { - this.handle = handle; - } - - @Override - public @NonNull SubjectCollection getUserSubjects() { - return this.handle.getUserSubjects().sponge(); - } - - @Override - public @NonNull SubjectCollection getGroupSubjects() { - return this.handle.getGroupSubjects().sponge(); - } - - @Override - public @NonNull Subject getDefaults() { - return this.handle.getRootDefaults().sponge(); - } - - @Override - public @NonNull SubjectCollection getSubjects(@NonNull String s) { - return this.handle.getCollection(s).sponge(); - } - - @Override - public @NonNull Map getKnownSubjects() { - return this.handle.getLoadedCollections().entrySet().stream() - .collect(ImmutableCollectors.toMap( - Map.Entry::getKey, - e -> e.getValue().sponge() - )); - } - - @Override - public Optional newDescriptionBuilder(@NonNull Object o) { - Optional container = Sponge.getGame().getPluginManager().fromInstance(o); - if (!container.isPresent()) { - throw new IllegalArgumentException("Couldn't find a plugin container for " + o.getClass().getSimpleName()); - } - - return Optional.of(new DescriptionBuilder(this.handle, container.get())); - } - - @Override - public @NonNull Optional getDescription(@NonNull String s) { - return this.handle.getDescription(s).map(LPPermissionDescription::sponge); - } - - @Override - public @NonNull Collection getDescriptions() { - return this.handle.getDescriptions().stream().map(LPPermissionDescription::sponge).collect(ImmutableCollectors.toSet()); - } - - @Override - public void registerContextCalculator(@NonNull ContextCalculator contextCalculator) { - this.handle.registerContextCalculator(contextCalculator); - } - - @Override - public boolean equals(Object o) { - return o == this || o instanceof PermissionServiceProxy && this.handle.equals(((PermissionServiceProxy) o).handle); - } - - @Override - public int hashCode() { - return this.handle.hashCode(); - } - - @Override - public String toString() { - return "luckperms.api6.PermissionServiceProxy(handle=" + this.handle + ")"; - } -} diff --git a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectCollectionProxy.java b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectCollectionProxy.java deleted file mode 100644 index b3133a715..000000000 --- a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectCollectionProxy.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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.sponge.service.proxy.api6; - -import me.lucko.luckperms.common.util.ImmutableCollectors; -import me.lucko.luckperms.sponge.service.CompatibilityUtil; -import me.lucko.luckperms.sponge.service.model.LPPermissionService; -import me.lucko.luckperms.sponge.service.model.LPSubject; -import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.service.context.Context; -import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.service.permission.SubjectCollection; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -@SuppressWarnings("unchecked") -public final class SubjectCollectionProxy implements SubjectCollection, ProxiedServiceObject { - private final LPPermissionService service; - private final LPSubjectCollection handle; - - public SubjectCollectionProxy(LPPermissionService service, LPSubjectCollection handle) { - this.service = service; - this.handle = handle; - } - - @Override - public @NonNull String getIdentifier() { - return this.handle.getIdentifier(); - } - - @Override - public @NonNull Subject get(@NonNull String s) { - // force load the subject. - // after this call, users will expect that the subject is loaded in memory. - return this.handle.loadSubject(s).thenApply(LPSubject::sponge).join(); - } - - @Override - public boolean hasRegistered(@NonNull String s) { - return this.handle.hasRegistered(s).join(); - } - - @SuppressWarnings("rawtypes") - @Override - public @NonNull Iterable getAllSubjects() { - // this will lazily load all subjects. it will initially just get the identifiers of each subject, and will initialize dummy - // providers for those identifiers. when any methods against the dummy are called, the actual data will be loaded. - // this behaviour should be replaced when CompletableFutures are added to Sponge - return (List) this.handle.getAllIdentifiers() - .thenApply(ids -> ids.stream() - .map(s -> new SubjectProxy(this.service, this.service.getReferenceFactory().obtain(getIdentifier(), s))) - .collect(ImmutableCollectors.toList()) - ).join(); - } - - @SuppressWarnings("rawtypes") - @Override - public @NonNull Map getAllWithPermission(@NonNull String s) { - // again, these methods will lazily load subjects. - return (Map) this.handle.getAllWithPermission(s) - .thenApply(map -> map.entrySet().stream() - .collect(ImmutableCollectors.toMap( - e -> new SubjectProxy(this.service, e.getKey()), - Map.Entry::getValue - )) - ).join(); - } - - @SuppressWarnings("rawtypes") - @Override - public @NonNull Map getAllWithPermission(@NonNull Set set, @NonNull String s) { - return (Map) this.handle.getAllWithPermission(CompatibilityUtil.convertContexts(set), s) - .thenApply(map -> map.entrySet().stream() - .collect(ImmutableCollectors.toMap( - e -> new SubjectProxy(this.service, e.getKey()), - Map.Entry::getValue - )) - ).join(); - } - - @Override - public @NonNull Subject getDefaults() { - return this.handle.getDefaults().sponge(); - } - - @Override - public boolean equals(Object o) { - return o == this || o instanceof SubjectCollectionProxy && this.handle.equals(((SubjectCollectionProxy) o).handle); - } - - @Override - public int hashCode() { - return this.handle.hashCode(); - } - - @Override - public String toString() { - return "luckperms.api6.SubjectCollectionProxy(handle=" + this.handle + ")"; - } -} diff --git a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectDataProxy.java b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectDataProxy.java deleted file mode 100644 index 4451b0531..000000000 --- a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectDataProxy.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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.sponge.service.proxy.api6; - -import me.lucko.luckperms.common.util.ImmutableCollectors; -import me.lucko.luckperms.sponge.service.CompatibilityUtil; -import me.lucko.luckperms.sponge.service.model.LPPermissionService; -import me.lucko.luckperms.sponge.service.model.LPSubject; -import me.lucko.luckperms.sponge.service.model.LPSubjectData; -import me.lucko.luckperms.sponge.service.model.LPSubjectReference; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.service.context.Context; -import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.service.permission.SubjectData; -import org.spongepowered.api.util.Tristate; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - -@SuppressWarnings("unchecked") -public final class SubjectDataProxy implements SubjectData, ProxiedServiceObject { - private final LPPermissionService service; - private final LPSubjectReference ref; - private final boolean enduring; - - public SubjectDataProxy(LPPermissionService service, LPSubjectReference ref, boolean enduring) { - this.service = service; - this.ref = ref; - this.enduring = enduring; - } - - private CompletableFuture handle() { - return this.enduring ? - this.ref.resolveLp().thenApply(LPSubject::getSubjectData) : - this.ref.resolveLp().thenApply(LPSubject::getTransientSubjectData); - } - - @SuppressWarnings("rawtypes") - @Override - public @NonNull Map, Map> getAllPermissions() { - return (Map) handle().thenApply(handle -> handle.getAllPermissions().entrySet().stream() - .collect(ImmutableCollectors.toMap( - e -> CompatibilityUtil.convertContexts(e.getKey()), - Map.Entry::getValue - ))).join(); - } - - @Override - public @NonNull Map getPermissions(@NonNull Set contexts) { - return handle().thenApply(handle -> handle.getPermissions(CompatibilityUtil.convertContexts(contexts))).join(); - } - - @Override - public boolean setPermission(@NonNull Set contexts, @NonNull String permission, @NonNull Tristate value) { - handle().thenCompose(handle -> handle.setPermission( - CompatibilityUtil.convertContexts(contexts), - permission, - CompatibilityUtil.convertTristate(value) - )); - return true; - } - - @Override - public boolean clearPermissions() { - handle().thenCompose(LPSubjectData::clearPermissions); - return true; - } - - @Override - public boolean clearPermissions(@NonNull Set contexts) { - handle().thenCompose(handle -> handle.clearPermissions(CompatibilityUtil.convertContexts(contexts))); - return true; - } - - @SuppressWarnings("rawtypes") - @Override - public @NonNull Map, List> getAllParents() { - return (Map) handle().thenApply(handle -> handle.getAllParents().entrySet().stream() - .collect(ImmutableCollectors.toMap( - e -> CompatibilityUtil.convertContexts(e.getKey()), - e -> e.getValue().stream() - .map(s -> new SubjectProxy(this.service, s)) - .collect(ImmutableCollectors.toList()) - ) - )).join(); - } - - @SuppressWarnings("rawtypes") - @Override - public @NonNull List getParents(@NonNull Set contexts) { - return (List) handle().thenApply(handle -> handle.getParents(CompatibilityUtil.convertContexts(contexts)).stream() - .map(s -> new SubjectProxy(this.service, s)) - .collect(ImmutableCollectors.toList())).join(); - } - - @Override - public boolean addParent(@NonNull Set contexts, @NonNull Subject parent) { - handle().thenCompose(handle -> handle.addParent( - CompatibilityUtil.convertContexts(contexts), - this.service.getReferenceFactory().obtain(parent) - )); - return true; - } - - @Override - public boolean removeParent(@NonNull Set contexts, @NonNull Subject parent) { - handle().thenCompose(handle -> handle.removeParent( - CompatibilityUtil.convertContexts(contexts), - this.service.getReferenceFactory().obtain(parent) - )); - return true; - } - - @Override - public boolean clearParents() { - handle().thenCompose(LPSubjectData::clearParents); - return true; - } - - @Override - public boolean clearParents(@NonNull Set contexts) { - handle().thenCompose(handle -> handle.clearParents(CompatibilityUtil.convertContexts(contexts))); - return true; - } - - @SuppressWarnings("rawtypes") - @Override - public @NonNull Map, Map> getAllOptions() { - return (Map) handle().thenApply(handle -> handle.getAllOptions().entrySet().stream() - .collect(ImmutableCollectors.toMap( - e -> CompatibilityUtil.convertContexts(e.getKey()), - Map.Entry::getValue - ))).join(); - } - - @Override - public @NonNull Map getOptions(@NonNull Set contexts) { - return handle().thenApply(handle -> handle.getOptions(CompatibilityUtil.convertContexts(contexts))).join(); - } - - @Override - public boolean setOption(@NonNull Set contexts, @NonNull String key, String value) { - if (value == null) { - handle().thenCompose(handle -> handle.unsetOption(CompatibilityUtil.convertContexts(contexts), key)); - } else { - handle().thenCompose(handle -> handle.setOption(CompatibilityUtil.convertContexts(contexts), key, value)); - } - return true; - } - - @Override - public boolean clearOptions(@NonNull Set contexts) { - handle().thenCompose(handle -> handle.clearOptions(CompatibilityUtil.convertContexts(contexts))); - return true; - } - - @Override - public boolean clearOptions() { - handle().thenCompose(LPSubjectData::clearOptions); - return true; - } - - @Override - public boolean equals(Object o) { - if (o == this) return true; - if (!(o instanceof SubjectDataProxy)) return false; - final SubjectDataProxy other = (SubjectDataProxy) o; - return this.ref.equals(other.ref) && this.enduring == other.enduring; - } - - @Override - public int hashCode() { - final int PRIME = 59; - int result = 1; - result = result * PRIME + this.ref.hashCode(); - result = result * PRIME + (this.enduring ? 79 : 97); - return result; - } - - @Override - public String toString() { - return "luckperms.api6.SubjectDataProxy(ref=" + this.ref + ", enduring=" + this.enduring + ")"; - } -} diff --git a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java b/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java deleted file mode 100644 index 237362660..000000000 --- a/sponge/sponge-service-api6/src/main/java/me/lucko/luckperms/sponge/service/proxy/api6/SubjectProxy.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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.sponge.service.proxy.api6; - -import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; -import me.lucko.luckperms.common.util.ImmutableCollectors; -import me.lucko.luckperms.sponge.service.CompatibilityUtil; -import me.lucko.luckperms.sponge.service.model.LPPermissionService; -import me.lucko.luckperms.sponge.service.model.LPSubject; -import me.lucko.luckperms.sponge.service.model.LPSubjectReference; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; -import me.lucko.luckperms.sponge.service.model.ProxiedSubject; - -import net.luckperms.api.query.QueryOptions; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.service.context.Context; -import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.service.permission.SubjectCollection; -import org.spongepowered.api.service.permission.SubjectData; -import org.spongepowered.api.util.Tristate; - -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - -@SuppressWarnings("unchecked") -public final class SubjectProxy implements Subject, ProxiedSubject, ProxiedServiceObject { - private final LPPermissionService service; - private final LPSubjectReference ref; - - private QueryOptionsSupplier queryOptionsSupplier = null; - - public SubjectProxy(LPPermissionService service, LPSubjectReference ref) { - this.service = service; - this.ref = ref; - } - - private CompletableFuture handle() { - return this.ref.resolveLp(); - } - - // lazy init - private QueryOptionsSupplier getContextsCache() { - if (this.queryOptionsSupplier == null) { - this.queryOptionsSupplier = this.service.getContextManager().getCacheFor(this); - } - return this.queryOptionsSupplier; - } - - @Override - public @NonNull LPSubjectReference asSubjectReference() { - return this.ref; - } - - @Override - public @NonNull QueryOptions getQueryOptions() { - return getContextsCache().getQueryOptions(); - } - - @Override - public @NonNull Optional getCommandSource() { - return handle().thenApply(LPSubject::getCommandSource).join(); - } - - @Override - public @NonNull SubjectCollection getContainingCollection() { - return this.service.getCollection(this.ref.getCollectionIdentifier()).sponge(); - } - - @Override - public SubjectData getSubjectData() { - return new SubjectDataProxy(this.service, this.ref, true); - } - - @Override - public SubjectData getTransientSubjectData() { - return new SubjectDataProxy(this.service, this.ref, false); - } - - @Override - public boolean hasPermission(@NonNull Set contexts, @NonNull String permission) { - return handle().thenApply(handle -> handle.getPermissionValue(CompatibilityUtil.convertContexts(contexts), permission).asBoolean()).join(); - } - - @Override - public boolean hasPermission(@NonNull String permission) { - return handle().thenApply(handle -> handle.getPermissionValue(getContextsCache().getContextSet(), permission).asBoolean()).join(); - } - - @Override - public @NonNull Tristate getPermissionValue(@NonNull Set contexts, @NonNull String permission) { - return handle().thenApply(handle -> CompatibilityUtil.convertTristate(handle.getPermissionValue(CompatibilityUtil.convertContexts(contexts), permission))).join(); - } - - @Override - public boolean isChildOf(@NonNull Subject parent) { - return handle().thenApply(handle -> handle.isChildOf( - getContextsCache().getContextSet(), - this.service.getReferenceFactory().obtain(parent) - )).join(); - } - - @Override - public boolean isChildOf(@NonNull Set contexts, @NonNull Subject parent) { - return handle().thenApply(handle -> handle.isChildOf( - CompatibilityUtil.convertContexts(contexts), - this.service.getReferenceFactory().obtain(parent) - )).join(); - } - - @SuppressWarnings("rawtypes") - @Override - public @NonNull List getParents() { - return (List) handle().thenApply(handle -> handle.getParents(getContextsCache().getContextSet()).stream() - .map(s -> new SubjectProxy(this.service, s)) - .collect(ImmutableCollectors.toList())).join(); - } - - @SuppressWarnings("rawtypes") - @Override - public @NonNull List getParents(@NonNull Set contexts) { - return (List) handle().thenApply(handle -> handle.getParents(CompatibilityUtil.convertContexts(contexts)).stream() - .map(s -> new SubjectProxy(this.service, s)) - .collect(ImmutableCollectors.toList())).join(); - } - - @Override - public @NonNull Optional getOption(@NonNull Set contexts, @NonNull String key) { - return handle().thenApply(handle -> handle.getOption(CompatibilityUtil.convertContexts(contexts), key)).join(); - } - - @Override - public @NonNull Optional getOption(@NonNull String key) { - return handle().thenApply(handle -> handle.getOption(getContextsCache().getContextSet(), key)).join(); - } - - @Override - public String getIdentifier() { - return this.ref.getSubjectIdentifier(); - } - - @Override - public @NonNull Set getActiveContexts() { - return CompatibilityUtil.convertContexts(getContextsCache().getContextSet()); - } - - @Override - public boolean equals(Object o) { - return o == this || o instanceof SubjectProxy && this.ref.equals(((SubjectProxy) o).ref); - } - - @Override - public int hashCode() { - return this.ref.hashCode(); - } - - @Override - public String toString() { - return "luckperms.api6.SubjectProxy(ref=" + this.ref + ")"; - } -} diff --git a/sponge/sponge-service-api7/build.gradle b/sponge/sponge-service-api7/build.gradle deleted file mode 100644 index a3fab4305..000000000 --- a/sponge/sponge-service-api7/build.gradle +++ /dev/null @@ -1,15 +0,0 @@ -repositories { - maven { url 'https://repo.spongepowered.org/maven' } -} - -dependencies { - implementation project(':common') - implementation project(':sponge:sponge-service') - - compileOnly('org.spongepowered:spongeapi:7.3.0') { - exclude(module: 'configurate-core') - exclude(module: 'configurate-hocon') - exclude(module: 'configurate-gson') - exclude(module: 'configurate-yaml') - } -} diff --git a/sponge/sponge-service-api6/build.gradle b/sponge/sponge-service-api8/build.gradle similarity index 70% rename from sponge/sponge-service-api6/build.gradle rename to sponge/sponge-service-api8/build.gradle index d423d5f03..27b324b0c 100644 --- a/sponge/sponge-service-api6/build.gradle +++ b/sponge/sponge-service-api8/build.gradle @@ -1,12 +1,12 @@ repositories { - maven { url 'https://repo.spongepowered.org/maven' } + maven { url 'https://repo.spongepowered.org/repository/maven-public/' } } dependencies { implementation project(':common') implementation project(':sponge:sponge-service') - compileOnly('org.spongepowered:spongeapi:6.0.0') { + compileOnly('org.spongepowered:spongeapi:8.0.0') { exclude(module: 'configurate-core') exclude(module: 'configurate-hocon') exclude(module: 'configurate-gson') diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/DescriptionBuilder.java b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/DescriptionBuilder.java similarity index 81% rename from sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/DescriptionBuilder.java rename to sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/DescriptionBuilder.java index cccb156f9..072813bcf 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/DescriptionBuilder.java +++ b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/DescriptionBuilder.java @@ -23,42 +23,34 @@ * SOFTWARE. */ -package me.lucko.luckperms.sponge.service.proxy.api7; +package me.lucko.luckperms.sponge.service.proxy.api8; import me.lucko.luckperms.common.context.ImmutableContextSetImpl; import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; import me.lucko.luckperms.sponge.service.model.LPPermissionService; +import me.lucko.luckperms.sponge.service.model.LPProxiedServiceObject; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; +import net.kyori.adventure.text.Component; import net.luckperms.api.util.Tristate; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.api.service.permission.PermissionDescription; import org.spongepowered.api.service.permission.PermissionService; -import org.spongepowered.api.text.Text; +import org.spongepowered.plugin.PluginContainer; import java.util.HashMap; import java.util.Map; import java.util.Objects; -public final class DescriptionBuilder implements PermissionDescription.Builder, ProxiedServiceObject { - - public static LPPermissionDescription registerDescription(LPPermissionService service, PermissionDescription description) { - if (!description.getOwner().isPresent()) { - return null; - } - return service.registerPermissionDescription(description.getId(), description.getDescription().orElse(null), description.getOwner().get()); - } - +public final class DescriptionBuilder implements PermissionDescription.Builder, LPProxiedServiceObject { private final @NonNull LPPermissionService service; private final @NonNull PluginContainer container; private final @NonNull Map roles = new HashMap<>(); private @Nullable String id = null; - private @Nullable Text description = null; + private @Nullable Component description = null; public DescriptionBuilder(LPPermissionService service, PluginContainer container) { this.service = Objects.requireNonNull(service, "service"); @@ -72,7 +64,7 @@ public DescriptionBuilder(LPPermissionService service, PluginContainer container } @Override - public PermissionDescription.@NonNull Builder description(@Nullable Text description) { + public PermissionDescription.@NonNull Builder description(@Nullable Component description) { this.description = description; return this; } @@ -84,6 +76,11 @@ public DescriptionBuilder(LPPermissionService service, PluginContainer container return this; } + @Override + public PermissionDescription.Builder defaultValue(org.spongepowered.api.util.Tristate defaultValue) { + throw new UnsupportedOperationException("LuckPerms does not support assigning a default value to permission descriptions"); + } + @Override public @NonNull PermissionDescription register() throws IllegalStateException { if (this.id == null) { @@ -121,13 +118,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - final int PRIME = 59; - int result = 1; - result = result * PRIME + this.container.hashCode(); - result = result * PRIME + this.roles.hashCode(); - result = result * PRIME + (this.id == null ? 43 : this.id.hashCode()); - result = result * PRIME + (this.description == null ? 43 : this.description.hashCode()); - return result; + return Objects.hash(this.container, this.roles, this.id, this.description); } @Override diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/PermissionDescriptionProxy.java b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionDescriptionProxy.java similarity index 65% rename from sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/PermissionDescriptionProxy.java rename to sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionDescriptionProxy.java index e9ea3fee7..9212a53df 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/PermissionDescriptionProxy.java +++ b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionDescriptionProxy.java @@ -23,25 +23,29 @@ * SOFTWARE. */ -package me.lucko.luckperms.sponge.service.proxy.api7; +package me.lucko.luckperms.sponge.service.proxy.api8; import me.lucko.luckperms.common.util.ImmutableCollectors; import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; import me.lucko.luckperms.sponge.service.model.LPPermissionService; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; +import me.lucko.luckperms.sponge.service.model.LPProxiedServiceObject; + +import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.plugin.PluginContainer; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.service.permission.PermissionDescription; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectReference; -import org.spongepowered.api.text.Text; +import org.spongepowered.api.util.Tristate; +import org.spongepowered.plugin.PluginContainer; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; -public final class PermissionDescriptionProxy implements PermissionDescription, ProxiedServiceObject { +public final class PermissionDescriptionProxy implements PermissionDescription, LPProxiedServiceObject { private final LPPermissionService service; private final LPPermissionDescription handle; @@ -51,22 +55,27 @@ public PermissionDescriptionProxy(LPPermissionService service, LPPermissionDescr } @Override - public @NonNull String getId() { + public @NonNull String id() { return this.handle.getId(); } @Override - public @NonNull Optional getDescription() { + public @NonNull Optional description() { return this.handle.getDescription(); } @Override - public @NonNull Optional getOwner() { + public @NonNull Optional owner() { return this.handle.getOwner(); } @Override - public @NonNull Map getAssignedSubjects(@NonNull String s) { + public Tristate defaultValue() { + return Tristate.UNDEFINED; + } + + @Override + public @NonNull Map assignedSubjects(@NonNull String s) { return this.handle.getAssignedSubjects(s).entrySet().stream() .collect(ImmutableCollectors.toMap( e -> new SubjectProxy(this.service, e.getKey().toReference()), @@ -74,6 +83,37 @@ public PermissionDescriptionProxy(LPPermissionService service, LPPermissionDescr )); } + @Override + public boolean query(Subject subj) { + return subj.hasPermission(this.handle.getId()); + } + + @Override + public boolean query(Subject subj, String parameter) { + Objects.requireNonNull(parameter, "parameter"); + return subj.hasPermission(this.handle.getId() + '.' + parameter); + } + + @Override + public boolean query(Subject subj, ResourceKey key) { + return query(subj, key.namespace() + '.' + key.value()); + } + + @Override + public boolean query(Subject subj, String... parameters) { + if (parameters.length == 0) { + return this.query(subj); + } else if (parameters.length == 1) { + return this.query(subj, parameters[0]); + } + + StringBuilder builder = new StringBuilder(this.handle.getId()); + for (String parameter : parameters) { + builder.append('.').append(parameter); + } + return subj.hasPermission(builder.toString()); + } + @Override @SuppressWarnings({"unchecked", "rawtypes"}) public @NonNull CompletableFuture> findAssignedSubjects(@NonNull String s) { @@ -92,6 +132,6 @@ public int hashCode() { @Override public String toString() { - return "luckperms.api7.PermissionDescriptionProxy(handle=" + this.handle + ")"; + return "luckperms.api8.PermissionDescriptionProxy(handle=" + this.handle + ")"; } } diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/PermissionServiceProxy.java b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionServiceProxy.java similarity index 72% rename from sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/PermissionServiceProxy.java rename to sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionServiceProxy.java index dfca717a2..0c5648a62 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/PermissionServiceProxy.java +++ b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionServiceProxy.java @@ -23,25 +23,28 @@ * SOFTWARE. */ -package me.lucko.luckperms.sponge.service.proxy.api7; +package me.lucko.luckperms.sponge.service.proxy.api8; import com.google.common.collect.ImmutableSet; import me.lucko.luckperms.common.util.ImmutableCollectors; +import me.lucko.luckperms.sponge.service.CompatibilityUtil; +import me.lucko.luckperms.sponge.service.PermissionAndContextService; import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; import me.lucko.luckperms.sponge.service.model.LPPermissionService; +import me.lucko.luckperms.sponge.service.model.LPProxiedServiceObject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.Sponge; -import org.spongepowered.api.plugin.PluginContainer; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.context.ContextCalculator; import org.spongepowered.api.service.permission.PermissionDescription; import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectCollection; import org.spongepowered.api.service.permission.SubjectReference; +import org.spongepowered.plugin.PluginContainer; import java.util.Collection; import java.util.Locale; @@ -52,7 +55,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; -public final class PermissionServiceProxy implements PermissionService, ProxiedServiceObject { +public final class PermissionServiceProxy implements PermissionAndContextService, LPProxiedServiceObject { private final LPPermissionService handle; public PermissionServiceProxy(LPPermissionService handle) { @@ -60,22 +63,22 @@ public PermissionServiceProxy(LPPermissionService handle) { } @Override - public @NonNull SubjectCollection getUserSubjects() { + public @NonNull SubjectCollection userSubjects() { return this.handle.getUserSubjects().sponge(); } @Override - public @NonNull SubjectCollection getGroupSubjects() { + public @NonNull SubjectCollection groupSubjects() { return this.handle.getGroupSubjects().sponge(); } @Override - public @NonNull Subject getDefaults() { + public @NonNull Subject defaults() { return this.handle.getRootDefaults().sponge(); } @Override - public @NonNull Predicate getIdentifierValidityPredicate() { + public @NonNull Predicate identifierValidityPredicate() { return this.handle.getIdentifierValidityPredicate(); } @@ -85,7 +88,7 @@ public CompletableFuture loadCollection(@NonNull String s) { } @Override - public @NonNull Optional getCollection(String s) { + public @NonNull Optional collection(String s) { return Optional.ofNullable(this.handle.getLoadedCollections().get(s.toLowerCase(Locale.ROOT))).map(LPSubjectCollection::sponge); } @@ -95,7 +98,7 @@ public CompletableFuture hasCollection(String s) { } @Override - public @NonNull Map getLoadedCollections() { + public @NonNull Map loadedCollections() { return this.handle.getLoadedCollections().entrySet().stream() .collect(ImmutableCollectors.toMap( Map.Entry::getKey, @@ -104,7 +107,7 @@ public CompletableFuture hasCollection(String s) { } @Override - public CompletableFuture> getAllIdentifiers() { + public CompletableFuture> allIdentifiers() { return CompletableFuture.completedFuture(ImmutableSet.copyOf(this.handle.getLoadedCollections().keySet())); } @@ -114,10 +117,11 @@ public CompletableFuture> getAllIdentifiers() { Objects.requireNonNull(subjectIdentifier, "subjectIdentifier"); // test the identifiers - String collection = collectionIdentifier.toLowerCase(Locale.ROOT); - if (collection.equals("user") && !this.handle.getUserSubjects().getIdentifierValidityPredicate().test(subjectIdentifier)) { + if (collectionIdentifier.equalsIgnoreCase(PermissionService.SUBJECTS_USER) && + !this.handle.getUserSubjects().getIdentifierValidityPredicate().test(subjectIdentifier)) { throw new IllegalArgumentException("Subject identifier '" + subjectIdentifier + "' does not pass the validity predicate for the user subject collection"); - } else if (collection.equals("group") && !this.handle.getGroupSubjects().getIdentifierValidityPredicate().test(subjectIdentifier)) { + } else if (collectionIdentifier.equalsIgnoreCase(PermissionService.SUBJECTS_GROUP) && + !this.handle.getGroupSubjects().getIdentifierValidityPredicate().test(subjectIdentifier)) { throw new IllegalArgumentException("Subject identifier '" + subjectIdentifier + "' does not pass the validity predicate for the group subject collection"); } @@ -126,27 +130,32 @@ public CompletableFuture> getAllIdentifiers() { } @Override - public PermissionDescription.Builder newDescriptionBuilder(@NonNull Object o) { - Optional container = Sponge.getGame().getPluginManager().fromInstance(o); - if (!container.isPresent()) { - throw new IllegalArgumentException("Couldn't find a plugin container for " + o.getClass().getSimpleName()); - } - - return new DescriptionBuilder(this.handle, container.get()); + public PermissionDescription.Builder newDescriptionBuilder(@NonNull PluginContainer container) { + return new DescriptionBuilder(this.handle, container); } @Override - public @NonNull Optional getDescription(@NonNull String s) { + public @NonNull Optional description(@NonNull String s) { return this.handle.getDescription(s).map(LPPermissionDescription::sponge); } @Override - public @NonNull Collection getDescriptions() { + public @NonNull Collection descriptions() { return this.handle.getDescriptions().stream().map(LPPermissionDescription::sponge).collect(ImmutableCollectors.toSet()); } @Override - public void registerContextCalculator(@NonNull ContextCalculator contextCalculator) { + public Set contexts() { + return CompatibilityUtil.convertContexts(this.handle.getContextsForCurrentCause()); + } + + @Override + public Set contextsFor(Cause cause) { + return CompatibilityUtil.convertContexts(this.handle.getContextsForCurrentCause()); + } + + @Override + public void registerContextCalculator(@NonNull ContextCalculator contextCalculator) { this.handle.registerContextCalculator(contextCalculator); } @@ -162,6 +171,6 @@ public int hashCode() { @Override public String toString() { - return "luckperms.api7.PermissionServiceProxy(handle=" + this.handle + ")"; + return "luckperms.api8.PermissionServiceProxy(handle=" + this.handle + ")"; } } diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectCollectionProxy.java b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/SubjectCollectionProxy.java similarity index 79% rename from sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectCollectionProxy.java rename to sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/SubjectCollectionProxy.java index fe66e8df7..e054193ab 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectCollectionProxy.java +++ b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/SubjectCollectionProxy.java @@ -23,16 +23,15 @@ * SOFTWARE. */ -package me.lucko.luckperms.sponge.service.proxy.api7; +package me.lucko.luckperms.sponge.service.proxy.api8; import me.lucko.luckperms.common.util.ImmutableCollectors; -import me.lucko.luckperms.sponge.service.CompatibilityUtil; +import me.lucko.luckperms.sponge.service.model.LPProxiedServiceObject; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.event.Cause; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectCollection; import org.spongepowered.api.service.permission.SubjectReference; @@ -46,7 +45,7 @@ import java.util.function.Predicate; @SuppressWarnings("unchecked") -public final class SubjectCollectionProxy implements SubjectCollection, ProxiedServiceObject { +public final class SubjectCollectionProxy implements SubjectCollection, LPProxiedServiceObject { private final LPSubjectCollection handle; public SubjectCollectionProxy(LPSubjectCollection handle) { @@ -54,12 +53,12 @@ public SubjectCollectionProxy(LPSubjectCollection handle) { } @Override - public @NonNull String getIdentifier() { + public @NonNull String identifier() { return this.handle.getIdentifier(); } @Override - public @NonNull Predicate getIdentifierValidityPredicate() { + public @NonNull Predicate identifierValidityPredicate() { return this.handle.getIdentifierValidityPredicate(); } @@ -69,7 +68,7 @@ public SubjectCollectionProxy(LPSubjectCollection handle) { } @Override - public @NonNull Optional getSubject(@NonNull String s) { + public @NonNull Optional subject(@NonNull String s) { return this.handle.getSubject(s).map(LPSubject::sponge); } @@ -79,18 +78,18 @@ public SubjectCollectionProxy(LPSubjectCollection handle) { } @Override - public @NonNull CompletableFuture> loadSubjects(@NonNull Set set) { + public @NonNull CompletableFuture> loadSubjects(@NonNull Iterable set) { return this.handle.loadSubjects(set).thenApply(subs -> subs.stream().collect(ImmutableCollectors.toMap(lpSubject -> lpSubject.getIdentifier().getName(), LPSubject::sponge))); } @Override - public @NonNull Collection getLoadedSubjects() { + public @NonNull Collection loadedSubjects() { return this.handle.getLoadedSubjects().stream().map(LPSubject::sponge).collect(ImmutableCollectors.toSet()); } @SuppressWarnings("rawtypes") @Override - public @NonNull CompletableFuture> getAllIdentifiers() { + public @NonNull CompletableFuture> allIdentifiers() { return (CompletableFuture) this.handle.getAllIdentifiers(); } @@ -101,23 +100,23 @@ public SubjectCollectionProxy(LPSubjectCollection handle) { throw new IllegalArgumentException("Subject identifier '" + subjectIdentifier + "' does not pass the validity predicate"); } - return this.handle.getService().getReferenceFactory().obtain(getIdentifier(), subjectIdentifier); + return this.handle.getService().getReferenceFactory().obtain(identifier(), subjectIdentifier); } @SuppressWarnings("rawtypes") @Override - public @NonNull CompletableFuture> getAllWithPermission(@NonNull String s) { + public @NonNull CompletableFuture> allWithPermission(@NonNull String s) { return (CompletableFuture) this.handle.getAllWithPermission(s); } @SuppressWarnings("rawtypes") @Override - public @NonNull CompletableFuture> getAllWithPermission(@NonNull Set set, @NonNull String s) { - return (CompletableFuture) this.handle.getAllWithPermission(CompatibilityUtil.convertContexts(set), s); + public @NonNull CompletableFuture> allWithPermission(@NonNull String s, @NonNull Cause cause) { + return (CompletableFuture) this.handle.getAllWithPermission(this.handle.getService().getContextsForCause(cause), s); } @Override - public @NonNull Map getLoadedWithPermission(@NonNull String s) { + public @NonNull Map loadedWithPermission(@NonNull String s) { return this.handle.getLoadedWithPermission(s).entrySet().stream() .collect(ImmutableCollectors.toMap( sub -> sub.getKey().sponge(), @@ -126,8 +125,8 @@ public SubjectCollectionProxy(LPSubjectCollection handle) { } @Override - public @NonNull Map getLoadedWithPermission(@NonNull Set set, @NonNull String s) { - return this.handle.getLoadedWithPermission(CompatibilityUtil.convertContexts(set), s).entrySet().stream() + public @NonNull Map loadedWithPermission(@NonNull String s, @NonNull Cause cause) { + return this.handle.getLoadedWithPermission(this.handle.getService().getContextsForCause(cause), s).entrySet().stream() .collect(ImmutableCollectors.toMap( sub -> sub.getKey().sponge(), Map.Entry::getValue @@ -135,7 +134,7 @@ public SubjectCollectionProxy(LPSubjectCollection handle) { } @Override - public @NonNull Subject getDefaults() { + public @NonNull Subject defaults() { return this.handle.getDefaults().sponge(); } @@ -156,7 +155,7 @@ public int hashCode() { @Override public String toString() { - return "luckperms.api7.SubjectCollectionProxy(handle=" + this.handle + ")"; + return "luckperms.api8.SubjectCollectionProxy(handle=" + this.handle + ")"; } } diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectDataProxy.java b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/SubjectDataProxy.java similarity index 68% rename from sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectDataProxy.java rename to sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/SubjectDataProxy.java index 588454ab9..a9e1d21dc 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectDataProxy.java +++ b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/SubjectDataProxy.java @@ -23,29 +23,36 @@ * SOFTWARE. */ -package me.lucko.luckperms.sponge.service.proxy.api7; +package me.lucko.luckperms.sponge.service.proxy.api8; +import com.google.common.collect.ImmutableMap; + +import me.lucko.luckperms.common.util.CompletableFutures; import me.lucko.luckperms.common.util.ImmutableCollectors; import me.lucko.luckperms.sponge.service.CompatibilityUtil; import me.lucko.luckperms.sponge.service.model.LPPermissionService; +import me.lucko.luckperms.sponge.service.model.LPProxiedServiceObject; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectData; import me.lucko.luckperms.sponge.service.model.LPSubjectReference; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectData; +import org.spongepowered.api.service.permission.SubjectReference; +import org.spongepowered.api.service.permission.TransferMethod; import org.spongepowered.api.util.Tristate; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; @SuppressWarnings("unchecked") -public final class SubjectDataProxy implements SubjectData, ProxiedServiceObject { +public final class SubjectDataProxy implements SubjectData, LPProxiedServiceObject { private final LPPermissionService service; private final LPSubjectReference ref; private final boolean enduring; @@ -62,9 +69,19 @@ private CompletableFuture handle() { this.ref.resolveLp().thenApply(LPSubject::getTransientSubjectData); } + @Override + public Subject subject() { + return handle().thenApply(handle -> handle.getParentSubject().sponge()).join(); + } + + @Override + public boolean isTransient() { + return !this.enduring; + } + @SuppressWarnings("rawtypes") @Override - public @NonNull Map, Map> getAllPermissions() { + public @NonNull Map, Map> allPermissions() { return (Map) handle().thenApply(handle -> handle.getAllPermissions().entrySet().stream() .collect(ImmutableCollectors.toMap( e -> CompatibilityUtil.convertContexts(e.getKey()), @@ -73,7 +90,7 @@ private CompletableFuture handle() { } @Override - public @NonNull Map getPermissions(@NonNull Set contexts) { + public @NonNull Map permissions(@NonNull Set contexts) { return handle().thenApply(handle -> handle.getPermissions(CompatibilityUtil.convertContexts(contexts))).join(); } @@ -86,6 +103,43 @@ private CompletableFuture handle() { )); } + @Override + public CompletableFuture setPermissions(Set contexts, Map permissions, TransferMethod method) { + CompletableFuture fut; + if (method == TransferMethod.OVERWRITE) { + fut = clearPermissions(contexts); + } else { + fut = CompletableFuture.completedFuture(true); + } + + return fut.thenCompose(bool -> permissions.entrySet().stream() + .map(entry -> setPermission(contexts, entry.getKey(), Tristate.fromBoolean(entry.getValue()))) + .collect(CompletableFutures.collector()) + ) + .thenApply(x -> true); + } + + @Override + public Tristate fallbackPermissionValue(Set contexts) { + // TODO: check subject type here to return a more appropriate value? + return Tristate.UNDEFINED; + } + + @Override + public Map, Tristate> allFallbackPermissionValues() { + return ImmutableMap.of(); + } + + @Override + public CompletableFuture setFallbackPermissionValue(Set contexts, Tristate fallback) { + throw new UnsupportedOperationException("LuckPerms does not support setting fallback permission values"); + } + + @Override + public CompletableFuture clearFallbackPermissionValues() { + return CompletableFuture.completedFuture(true); + } + @Override public @NonNull CompletableFuture clearPermissions() { return handle().thenCompose(LPSubjectData::clearPermissions); @@ -98,7 +152,7 @@ private CompletableFuture handle() { @SuppressWarnings("rawtypes") @Override - public @NonNull Map, List> getAllParents() { + public @NonNull Map, List> allParents() { return (Map) handle().thenApply(handle -> handle.getAllParents().entrySet().stream() .collect(ImmutableCollectors.toMap( e -> CompatibilityUtil.convertContexts(e.getKey()), @@ -108,10 +162,15 @@ private CompletableFuture handle() { @SuppressWarnings("rawtypes") @Override - public @NonNull List getParents(@NonNull Set contexts) { + public @NonNull List parents(@NonNull Set contexts) { return (List) handle().thenApply(handle -> handle.getParents(CompatibilityUtil.convertContexts(contexts))).join(); } + @Override + public CompletableFuture setParents(Set contexts, List parents, TransferMethod method) { + return null; + } + @Override public @NonNull CompletableFuture addParent(@NonNull Set contexts, org.spongepowered.api.service.permission.@NonNull SubjectReference ref) { return handle().thenCompose(handle -> handle.addParent(CompatibilityUtil.convertContexts(contexts), this.service.getReferenceFactory().obtain(ref))); @@ -134,7 +193,7 @@ private CompletableFuture handle() { @SuppressWarnings("rawtypes") @Override - public @NonNull Map, Map> getAllOptions() { + public @NonNull Map, Map> allOptions() { return (Map) handle().thenApply(handle -> handle.getAllOptions().entrySet().stream() .collect(ImmutableCollectors.toMap( e -> CompatibilityUtil.convertContexts(e.getKey()), @@ -143,7 +202,7 @@ private CompletableFuture handle() { } @Override - public @NonNull Map getOptions(@NonNull Set contexts) { + public @NonNull Map options(@NonNull Set contexts) { return handle().thenApply(handle -> handle.getOptions(CompatibilityUtil.convertContexts(contexts))).join(); } @@ -156,6 +215,11 @@ private CompletableFuture handle() { } } + @Override + public CompletableFuture setOptions(Set contexts, Map options, TransferMethod method) { + return null; + } + @Override public @NonNull CompletableFuture clearOptions() { return handle().thenCompose(LPSubjectData::clearOptions); @@ -166,6 +230,16 @@ private CompletableFuture handle() { return handle().thenCompose(handle -> handle.clearOptions(CompatibilityUtil.convertContexts(contexts))); } + @Override + public CompletableFuture copyFrom(SubjectData other, TransferMethod method) { + return null; + } + + @Override + public CompletableFuture moveFrom(SubjectData other, TransferMethod method) { + return null; + } + @Override public boolean equals(Object o) { if (o == this) return true; @@ -176,15 +250,11 @@ public boolean equals(Object o) { @Override public int hashCode() { - final int PRIME = 59; - int result = 1; - result = result * PRIME + this.ref.hashCode(); - result = result * PRIME + (this.enduring ? 79 : 97); - return result; + return Objects.hash(this.ref, this.enduring); } @Override public String toString() { - return "luckperms.api7.SubjectDataProxy(ref=" + this.ref + ", enduring=" + this.enduring + ")"; + return "luckperms.api8.SubjectDataProxy(ref=" + this.ref + ", enduring=" + this.enduring + ")"; } } diff --git a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/SubjectProxy.java similarity index 55% rename from sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java rename to sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/SubjectProxy.java index e8492738c..f80280e2e 100644 --- a/sponge/sponge-service-api7/src/main/java/me/lucko/luckperms/sponge/service/proxy/api7/SubjectProxy.java +++ b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/SubjectProxy.java @@ -23,21 +23,23 @@ * SOFTWARE. */ -package me.lucko.luckperms.sponge.service.proxy.api7; +package me.lucko.luckperms.sponge.service.proxy.api8; -import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.sponge.service.CompatibilityUtil; import me.lucko.luckperms.sponge.service.model.LPPermissionService; +import me.lucko.luckperms.sponge.service.model.LPProxiedServiceObject; +import me.lucko.luckperms.sponge.service.model.LPProxiedSubject; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectReference; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; -import me.lucko.luckperms.sponge.service.model.ProxiedSubject; +import me.lucko.luckperms.sponge.service.model.LPSubjectUser; import net.luckperms.api.query.QueryOptions; import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.Cause; import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectCollection; import org.spongepowered.api.service.permission.SubjectData; @@ -50,12 +52,10 @@ import java.util.concurrent.CompletableFuture; @SuppressWarnings("unchecked") -public final class SubjectProxy implements Subject, ProxiedSubject, ProxiedServiceObject { +public final class SubjectProxy implements Subject, LPProxiedSubject, LPProxiedServiceObject { private final LPPermissionService service; private final LPSubjectReference ref; - private QueryOptionsSupplier queryOptionsSupplier; - public SubjectProxy(LPPermissionService service, LPSubjectReference ref) { this.service = service; this.ref = ref; @@ -65,14 +65,6 @@ private CompletableFuture handle() { return this.ref.resolveLp(); } - // lazy init - private QueryOptionsSupplier queryOptionsCache() { - if (this.queryOptionsSupplier == null) { - this.queryOptionsSupplier = this.service.getContextManager().getCacheFor(this); - } - return this.queryOptionsSupplier; - } - @Override public @NonNull LPSubjectReference asSubjectReference() { return this.ref; @@ -80,17 +72,12 @@ private QueryOptionsSupplier queryOptionsCache() { @Override public @NonNull QueryOptions getQueryOptions() { - return queryOptionsCache().getQueryOptions(); - } - - @Override - public @NonNull Optional getCommandSource() { - return handle().thenApply(LPSubject::getCommandSource).join(); + return this.service.getContextManager().getQueryOptions(this); } @Override - public @NonNull SubjectCollection getContainingCollection() { - return this.service.getCollection(this.ref.getCollectionIdentifier()).sponge(); + public @NonNull SubjectCollection containingCollection() { + return this.service.getCollection(this.ref.collectionIdentifier()).sponge(); } @Override @@ -99,75 +86,77 @@ public boolean isSubjectDataPersisted() { } @Override - public SubjectData getSubjectData() { + public SubjectData subjectData() { return new SubjectDataProxy(this.service, this.ref, true); } @Override - public SubjectData getTransientSubjectData() { + public SubjectData transientSubjectData() { return new SubjectDataProxy(this.service, this.ref, false); } @Override - public boolean hasPermission(@NonNull Set contexts, @NonNull String permission) { - return handle().thenApply(handle -> handle.getPermissionValue(CompatibilityUtil.convertContexts(contexts), permission).asBoolean()).join(); + public @NonNull Tristate permissionValue(@NonNull String permission, @NonNull Cause cause) { + return handle().thenApply(handle -> CompatibilityUtil.convertTristate(handle.getPermissionValue(this.service.getContextsForCause(cause), permission))).join(); } @Override - public boolean hasPermission(@NonNull String permission) { - return handle().thenApply(handle -> handle.getPermissionValue(queryOptionsCache().getContextSet(), permission).asBoolean()).join(); - } - - @Override - public @NonNull Tristate getPermissionValue(@NonNull Set contexts, @NonNull String permission) { + public @NonNull Tristate permissionValue(@NonNull String permission, @NonNull Set contexts) { return handle().thenApply(handle -> CompatibilityUtil.convertTristate(handle.getPermissionValue(CompatibilityUtil.convertContexts(contexts), permission))).join(); } @Override - public boolean isChildOf(@NonNull SubjectReference parent) { - return handle().thenApply(handle -> handle.isChildOf(queryOptionsCache().getContextSet(), this.service.getReferenceFactory().obtain(parent))).join(); + public boolean isChildOf(@NonNull SubjectReference parent, @NonNull Cause cause) { + return handle().thenApply(handle -> handle.isChildOf(this.service.getContextsForCause(cause), this.service.getReferenceFactory().obtain(parent))).join(); } @Override - public boolean isChildOf(@NonNull Set contexts, @NonNull SubjectReference parent) { + public boolean isChildOf(@NonNull SubjectReference parent, @NonNull Set contexts) { return handle().thenApply(handle -> handle.isChildOf(CompatibilityUtil.convertContexts(contexts), this.service.getReferenceFactory().obtain(parent))).join(); } - @SuppressWarnings("rawtypes") @Override - public @NonNull List getParents() { - return (List) handle().thenApply(handle -> handle.getParents(queryOptionsCache().getContextSet())).join(); + public List parents(@NonNull Cause cause) { + return handle().thenApply(handle -> handle.getParents(this.service.getContextsForCause(cause))).join(); } - @SuppressWarnings("rawtypes") @Override - public @NonNull List getParents(@NonNull Set contexts) { - return (List) handle().thenApply(handle -> handle.getParents(CompatibilityUtil.convertContexts(contexts))).join(); + public @NonNull List parents(@NonNull Set contexts) { + return handle().thenApply(handle -> handle.getParents(CompatibilityUtil.convertContexts(contexts))).join(); } @Override - public @NonNull Optional getOption(@NonNull Set contexts, @NonNull String key) { - return handle().thenApply(handle -> handle.getOption(CompatibilityUtil.convertContexts(contexts), key)).join(); + public Optional option(@NonNull String key, @NonNull Cause cause) { + return handle().thenApply(handle -> handle.getOption(this.service.getContextsForCause(cause), key)).join(); } @Override - public @NonNull Optional getOption(@NonNull String key) { - return handle().thenApply(handle -> handle.getOption(queryOptionsCache().getContextSet(), key)).join(); + public @NonNull Optional option(@NonNull String key, @NonNull Set contexts) { + return handle().thenApply(handle -> handle.getOption(CompatibilityUtil.convertContexts(contexts), key)).join(); } @Override - public String getIdentifier() { - return this.ref.getSubjectIdentifier(); + public String identifier() { + return this.ref.subjectIdentifier(); } @Override - public @NonNull Optional getFriendlyIdentifier() { + public @NonNull Optional friendlyIdentifier() { return handle().thenApply(LPSubject::getFriendlyIdentifier).join(); } @Override - public @NonNull Set getActiveContexts() { - return CompatibilityUtil.convertContexts(queryOptionsCache().getContextSet()); + public Optional associatedObject() { + if (this.ref.collectionIdentifier().equals(PermissionService.SUBJECTS_USER)) { + LPSubject lpSubject = handle().join(); + if (lpSubject instanceof LPSubjectUser) { + ServerPlayer player = ((LPSubjectUser) lpSubject).resolvePlayer().orElse(null); + if (player != null) { + return Optional.of(player); + } + } + } + return Optional.empty(); } @Override @@ -182,6 +171,6 @@ public int hashCode() { @Override public String toString() { - return "luckperms.api7.SubjectProxy(ref=" + this.ref + ")"; + return "luckperms.api8.SubjectProxy(ref=" + this.ref + ")"; } } diff --git a/sponge/sponge-service/build.gradle b/sponge/sponge-service/build.gradle index 88c3f2e04..4557ad3d6 100644 --- a/sponge/sponge-service/build.gradle +++ b/sponge/sponge-service/build.gradle @@ -1,11 +1,11 @@ repositories { - maven { url 'https://repo.spongepowered.org/maven' } + maven { url 'https://repo.spongepowered.org/repository/maven-public/' } } dependencies { implementation project(':common') - compileOnly('org.spongepowered:spongeapi:7.2.0') { + compileOnly('org.spongepowered:spongeapi:8.0.0') { exclude(module: 'configurate-core') exclude(module: 'configurate-hocon') exclude(module: 'configurate-gson') diff --git a/sponge/sponge-service-api7/src/main/java/org/spongepowered/api/event/permission/SubjectDataUpdateEvent.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/PermissionAndContextService.java similarity index 76% rename from sponge/sponge-service-api7/src/main/java/org/spongepowered/api/event/permission/SubjectDataUpdateEvent.java rename to sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/PermissionAndContextService.java index 987acaeb3..d567e9bc8 100644 --- a/sponge/sponge-service-api7/src/main/java/org/spongepowered/api/event/permission/SubjectDataUpdateEvent.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/PermissionAndContextService.java @@ -23,14 +23,11 @@ * SOFTWARE. */ -package org.spongepowered.api.event.permission; +package me.lucko.luckperms.sponge.service; -import org.spongepowered.api.event.Event; -import org.spongepowered.api.service.permission.SubjectData; +import org.spongepowered.api.service.context.ContextService; +import org.spongepowered.api.service.permission.PermissionService; -// Copy of https://github.com/SpongePowered/SpongeAPI/blob/api-8/src/main/java/org/spongepowered/api/event/permission/SubjectDataUpdateEvent.java -public interface SubjectDataUpdateEvent extends Event { +public interface PermissionAndContextService extends PermissionService, ContextService { - SubjectData getUpdatedData(); - -} \ No newline at end of file +} diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionDescription.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionDescription.java index 72de947ad..24b0b39fa 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionDescription.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionDescription.java @@ -25,9 +25,10 @@ package me.lucko.luckperms.sponge.service.model; -import org.spongepowered.api.plugin.PluginContainer; +import net.kyori.adventure.text.Component; + import org.spongepowered.api.service.permission.PermissionDescription; -import org.spongepowered.api.text.Text; +import org.spongepowered.plugin.PluginContainer; import java.util.Map; import java.util.Optional; @@ -44,7 +45,7 @@ public interface LPPermissionDescription { String getId(); - Optional getDescription(); + Optional getDescription(); Optional getOwner(); diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java index 8b4159bf6..1eed19a27 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java @@ -30,14 +30,18 @@ import me.lucko.luckperms.common.context.manager.ContextManager; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.sponge.service.PermissionAndContextService; import me.lucko.luckperms.sponge.service.reference.SubjectReferenceFactory; -import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.plugin.PluginContainer; +import net.kyori.adventure.text.Component; +import net.luckperms.api.context.ImmutableContextSet; + +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.Cause; import org.spongepowered.api.service.context.ContextCalculator; import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.text.Text; +import org.spongepowered.plugin.PluginContainer; import java.util.Optional; import java.util.function.Predicate; @@ -49,11 +53,11 @@ public interface LPPermissionService { LuckPermsPlugin getPlugin(); - ContextManager getContextManager(); + ContextManager getContextManager(); SubjectReferenceFactory getReferenceFactory(); - PermissionService sponge(); + PermissionAndContextService sponge(); LPSubjectCollection getUserSubjects(); @@ -69,13 +73,19 @@ public interface LPPermissionService { ImmutableMap getLoadedCollections(); - LPPermissionDescription registerPermissionDescription(String id, Text description, PluginContainer owner); + LPPermissionDescription registerPermissionDescription(String id, Component description, PluginContainer owner); Optional getDescription(String permission); ImmutableCollection getDescriptions(); - void registerContextCalculator(ContextCalculator calculator); + void registerContextCalculator(ContextCalculator calculator); + + ImmutableContextSet getContextsForCause(Cause cause); + + ImmutableContextSet getContextsForCurrentCause(); + + void fireUpdateEvent(LPSubjectData subjectData); void invalidateAllCaches(); } diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/ProxiedServiceObject.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPProxiedServiceObject.java similarity index 97% rename from sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/ProxiedServiceObject.java rename to sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPProxiedServiceObject.java index 66960fd5f..c851762c7 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/ProxiedServiceObject.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPProxiedServiceObject.java @@ -28,6 +28,6 @@ /** * Marks that an object is a proxy implementation for a PermissionService related class. */ -public interface ProxiedServiceObject { +public interface LPProxiedServiceObject { } diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/ProxiedSubject.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPProxiedSubject.java similarity index 95% rename from sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/ProxiedSubject.java rename to sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPProxiedSubject.java index edf2fe303..c717401af 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/ProxiedSubject.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPProxiedSubject.java @@ -33,7 +33,7 @@ /** * Marks that an object is a proxied representation of a {@link Subject}. */ -public interface ProxiedSubject extends Subject, ProxiedServiceObject { +public interface LPProxiedSubject extends Subject, LPProxiedServiceObject { @Override @NonNull LPSubjectReference asSubjectReference(); diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java index 9713d238e..42c5daa68 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubject.java @@ -33,7 +33,6 @@ import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; -import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.service.permission.Subject; import java.util.Optional; @@ -43,7 +42,7 @@ */ public interface LPSubject { - ProxiedSubject sponge(); + LPProxiedSubject sponge(); LPPermissionService getService(); @@ -59,10 +58,6 @@ default Optional getFriendlyIdentifier() { return Optional.empty(); } - default Optional getCommandSource() { - return Optional.empty(); - } - LPSubjectCollection getParentCollection(); LPSubjectData getSubjectData(); diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectCollection.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectCollection.java index cff2f416e..a94e0b109 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectCollection.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectCollection.java @@ -35,7 +35,6 @@ import org.spongepowered.api.service.permission.SubjectCollection; import java.util.Optional; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; @@ -71,7 +70,7 @@ default boolean isDefaultsCollection() { CompletableFuture hasRegistered(String identifier); - CompletableFuture> loadSubjects(Set identifiers); + CompletableFuture> loadSubjects(Iterable identifiers); ImmutableCollection getLoadedSubjects(); diff --git a/sponge/sponge-service-api6/src/main/java/org/spongepowered/api/service/permission/SubjectReference.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectUser.java similarity index 76% rename from sponge/sponge-service-api6/src/main/java/org/spongepowered/api/service/permission/SubjectReference.java rename to sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectUser.java index 03929c5a5..7d81230c6 100644 --- a/sponge/sponge-service-api6/src/main/java/org/spongepowered/api/service/permission/SubjectReference.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPSubjectUser.java @@ -23,20 +23,14 @@ * SOFTWARE. */ -package org.spongepowered.api.service.permission; +package me.lucko.luckperms.sponge.service.model; -import java.util.concurrent.CompletableFuture; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; -/** - * This is included, as the interface didn't exist in API6. - * We just shade it into the LP jar so we can still implement it. :) - */ -public interface SubjectReference { - - String getCollectionIdentifier(); +import java.util.Optional; - String getSubjectIdentifier(); +public interface LPSubjectUser extends LPSubject { - CompletableFuture resolve(); + Optional resolvePlayer(); -} \ No newline at end of file +} diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/CachedSubjectReference.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/CachedSubjectReference.java index 588389085..6586f8fb6 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/CachedSubjectReference.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/CachedSubjectReference.java @@ -76,12 +76,12 @@ final class CachedSubjectReference implements LPSubjectReference { } @Override - public @NonNull String getCollectionIdentifier() { + public @NonNull String collectionIdentifier() { return this.collectionIdentifier; } @Override - public @NonNull String getSubjectIdentifier() { + public @NonNull String subjectIdentifier() { return this.subjectIdentifier; } @@ -158,8 +158,8 @@ public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof LPSubjectReference)) return false; final LPSubjectReference other = (LPSubjectReference) o; - return this.collectionIdentifier.equals(other.getCollectionIdentifier()) && - this.subjectIdentifier.equals(other.getSubjectIdentifier()); + return this.collectionIdentifier.equals(other.collectionIdentifier()) && + this.subjectIdentifier.equals(other.subjectIdentifier()); } @Override diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java index 9efa6d95b..2a92f2e53 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/reference/SubjectReferenceFactory.java @@ -30,9 +30,9 @@ import me.lucko.luckperms.common.util.CaffeineFactory; import me.lucko.luckperms.sponge.service.model.LPPermissionService; +import me.lucko.luckperms.sponge.service.model.LPProxiedSubject; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectReference; -import me.lucko.luckperms.sponge.service.model.ProxiedSubject; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectReference; @@ -86,11 +86,11 @@ public LPSubjectReference obtain(LPSubject subject) { public LPSubjectReference obtain(Subject subject) { Objects.requireNonNull(subject, "subject"); - if (subject instanceof ProxiedSubject) { - return ((ProxiedSubject) subject).asSubjectReference(); + if (subject instanceof LPProxiedSubject) { + return ((LPProxiedSubject) subject).asSubjectReference(); } - return obtain(subject.getContainingCollection().getIdentifier(), subject.getIdentifier()); + return obtain(subject.containingCollection().identifier(), subject.identifier()); } public LPSubjectReference obtain(SubjectReference reference) { @@ -98,7 +98,7 @@ public LPSubjectReference obtain(SubjectReference reference) { if (reference instanceof LPSubjectReference) { return (LPSubjectReference) reference; } else { - return obtain(reference.getCollectionIdentifier(), reference.getSubjectIdentifier()); + return obtain(reference.collectionIdentifier(), reference.subjectIdentifier()); } } @@ -119,23 +119,15 @@ private static final class SubjectReferenceAttributes { private SubjectReferenceAttributes(String collectionId, String id) { this.collectionId = collectionId.toLowerCase(Locale.ROOT); this.id = id.toLowerCase(Locale.ROOT); - this.hashCode = calculateHashCode(); + this.hashCode = Objects.hash(this.collectionId, this.id); } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof SubjectReferenceAttributes)) return false; - final SubjectReferenceAttributes other = (SubjectReferenceAttributes) o; - return this.collectionId.equals(other.collectionId) && this.id.equals(other.id); - } - - private int calculateHashCode() { - final int PRIME = 59; - int result = 1; - result = result * PRIME + this.collectionId.hashCode(); - result = result * PRIME + this.id.hashCode(); - return result; + final SubjectReferenceAttributes that = (SubjectReferenceAttributes) o; + return this.collectionId.equals(that.collectionId) && this.id.equals(that.id); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java index fae255ed2..d74cd065d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java @@ -29,33 +29,30 @@ import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; import me.lucko.luckperms.common.plugin.classpath.ClassPathAppender; -import me.lucko.luckperms.common.plugin.classpath.ReflectionClassPathAppender; +import me.lucko.luckperms.common.plugin.logging.Log4jPluginLogger; import me.lucko.luckperms.common.plugin.logging.PluginLogger; -import me.lucko.luckperms.common.plugin.logging.Slf4jPluginLogger; -import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; import me.lucko.luckperms.common.util.MoreFiles; -import org.slf4j.Logger; +import net.luckperms.api.platform.Platform; + +import org.apache.logging.log4j.Logger; import org.spongepowered.api.Game; -import org.spongepowered.api.Platform; +import org.spongepowered.api.Platform.Component; import org.spongepowered.api.Server; -import org.spongepowered.api.Sponge; import org.spongepowered.api.config.ConfigDir; import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.Order; -import org.spongepowered.api.event.game.state.GamePreInitializationEvent; -import org.spongepowered.api.event.game.state.GameStoppingServerEvent; -import org.spongepowered.api.plugin.Dependency; -import org.spongepowered.api.plugin.Plugin; -import org.spongepowered.api.plugin.PluginContainer; +import org.spongepowered.api.event.lifecycle.ConstructPluginEvent; +import org.spongepowered.api.event.lifecycle.StoppingEngineEvent; import org.spongepowered.api.profile.GameProfile; -import org.spongepowered.api.scheduler.AsynchronousExecutor; -import org.spongepowered.api.scheduler.Scheduler; -import org.spongepowered.api.scheduler.SpongeExecutorService; -import org.spongepowered.api.scheduler.SynchronousExecutor; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.builtin.jvm.Plugin; +import org.spongepowered.plugin.metadata.PluginMetadata; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Path; import java.time.Instant; import java.util.ArrayList; @@ -69,18 +66,7 @@ /** * Bootstrap plugin for LuckPerms running on Sponge. */ -@Plugin( - id = "luckperms", - name = "LuckPerms", - version = "@version@", - authors = "Luck", - description = "A permissions plugin", - url = "https://luckperms.net", - dependencies = { - // explicit dependency on spongeapi with no defined API version - @Dependency(id = "spongeapi") - } -) +@Plugin("luckperms") public class LPSpongeBootstrap implements LuckPermsBootstrap { /** @@ -91,7 +77,7 @@ public class LPSpongeBootstrap implements LuckPermsBootstrap { /** * A scheduler adapter for the platform */ - private final SchedulerAdapter schedulerAdapter; + private final SpongeSchedulerAdapter schedulerAdapter; /** * The plugin class path appender @@ -115,33 +101,27 @@ public class LPSpongeBootstrap implements LuckPermsBootstrap { /** * Reference to the central {@link Game} instance in the API */ - @Inject - private Game game; + private final Game game; /** - * Reference to the sponge scheduler + * Injected plugin container for the plugin */ - private final Scheduler spongeScheduler; + private final PluginContainer pluginContainer; /** * Injected configuration directory for the plugin */ - @Inject - @ConfigDir(sharedRoot = false) - private Path configDirectory; - - /** - * Injected plugin container for the plugin - */ - @Inject - private PluginContainer pluginContainer; + private final Path configDirectory; @Inject - public LPSpongeBootstrap(Logger logger, @SynchronousExecutor SpongeExecutorService syncExecutor, @AsynchronousExecutor SpongeExecutorService asyncExecutor) { - this.logger = new Slf4jPluginLogger(logger); - this.spongeScheduler = Sponge.getScheduler(); - this.schedulerAdapter = new SpongeSchedulerAdapter(this, this.spongeScheduler, syncExecutor, asyncExecutor); - this.classPathAppender = new ReflectionClassPathAppender(this); + public LPSpongeBootstrap(Logger logger, Game game, PluginContainer pluginContainer, @ConfigDir(sharedRoot = false) Path configDirectory) { + this.logger = new Log4jPluginLogger(logger); + this.game = game; + this.pluginContainer = pluginContainer; + this.configDirectory = configDirectory; + + this.schedulerAdapter = new SpongeSchedulerAdapter(this.game, this.pluginContainer); + this.classPathAppender = new SpongeClassPathAppender(this); this.plugin = new LPSpongePlugin(this); } @@ -153,7 +133,7 @@ public PluginLogger getPluginLogger() { } @Override - public SchedulerAdapter getScheduler() { + public SpongeSchedulerAdapter getScheduler() { return this.schedulerAdapter; } @@ -163,9 +143,8 @@ public ClassPathAppender getClassPathAppender() { } // lifecycle - @Listener(order = Order.FIRST) - public void onEnable(GamePreInitializationEvent event) { + public void onEnable(ConstructPluginEvent event) { this.startTime = Instant.now(); try { this.plugin.load(); @@ -180,13 +159,8 @@ public void onEnable(GamePreInitializationEvent event) { } } - @Listener(order = Order.LATE) - public void onLateEnable(GamePreInitializationEvent event) { - this.plugin.lateEnable(); - } - @Listener - public void onDisable(GameStoppingServerEvent event) { + public void onDisable(StoppingEngineEvent event) { this.plugin.disable(); } @@ -207,22 +181,22 @@ public Game getGame() { } public Optional getServer() { - return this.game.isServerAvailable() ? Optional.of(this.game.getServer()) : Optional.empty(); - } - - public Scheduler getSpongeScheduler() { - return this.spongeScheduler; + return this.game.isServerAvailable() ? Optional.of(this.game.server()) : Optional.empty(); } public PluginContainer getPluginContainer() { return this.pluginContainer; } + public void registerListeners(Object obj) { + this.game.eventManager().registerListeners(this.pluginContainer, obj); + } + // provide information about the plugin @Override public String getVersion() { - return "@version@"; + return this.pluginContainer.metadata().version().toString(); } @Override @@ -233,25 +207,26 @@ public Instant getStartupTime() { // provide information about the platform @Override - public net.luckperms.api.platform.Platform.Type getType() { - return net.luckperms.api.platform.Platform.Type.SPONGE; + public Platform.Type getType() { + return Platform.Type.SPONGE; } @Override public String getServerBrand() { - return this.game.getPlatform().getContainer(Platform.Component.IMPLEMENTATION).getName(); + PluginMetadata brandMetadata = this.game.platform().container(Component.IMPLEMENTATION).metadata(); + return brandMetadata.name().orElseGet(brandMetadata::id); } @Override public String getServerVersion() { - PluginContainer api = this.game.getPlatform().getContainer(Platform.Component.API); - PluginContainer impl = this.game.getPlatform().getContainer(Platform.Component.IMPLEMENTATION); - return api.getName() + ": " + api.getVersion().orElse("null") + " - " + impl.getName() + ": " + impl.getVersion().orElse("null"); + PluginMetadata api = this.game.platform().container(Component.API).metadata(); + PluginMetadata impl = this.game.platform().container(Component.IMPLEMENTATION).metadata(); + return api.name().orElse("API") + ": " + api.version() + " - " + impl.name().orElse("Impl") + ": " + impl.version(); } @Override public Path getDataDirectory() { - Path dataDirectory = this.game.getGameDirectory().toAbsolutePath().resolve("luckperms"); + Path dataDirectory = this.game.gameDirectory().toAbsolutePath().resolve("luckperms"); try { MoreFiles.createDirectoriesIfNotExists(dataDirectory); } catch (IOException e) { @@ -266,14 +241,19 @@ public Path getConfigDirectory() { } @Override - public Optional getPlayer(UUID uniqueId) { - return getServer().flatMap(s -> s.getPlayer(uniqueId)); + public InputStream getResourceStream(String path) { + return getClass().getClassLoader().getResourceAsStream(path); + } + + @Override + public Optional getPlayer(UUID uniqueId) { + return getServer().flatMap(s -> s.player(uniqueId)); } @Override public Optional lookupUniqueId(String username) { - return getServer().flatMap(server -> server.getGameProfileManager().get(username) - .thenApply(p -> Optional.of(p.getUniqueId())) + return getServer().flatMap(server -> server.gameProfileManager().profile(username) + .thenApply(p -> Optional.of(p.uniqueId())) .exceptionally(x -> Optional.empty()) .join() ); @@ -281,8 +261,8 @@ public Optional lookupUniqueId(String username) { @Override public Optional lookupUsername(UUID uniqueId) { - return getServer().flatMap(server -> server.getGameProfileManager().get(uniqueId) - .thenApply(GameProfile::getName) + return getServer().flatMap(server -> server.gameProfileManager().profile(uniqueId) + .thenApply(GameProfile::name) .exceptionally(x -> Optional.empty()) .join() ); @@ -290,16 +270,16 @@ public Optional lookupUsername(UUID uniqueId) { @Override public int getPlayerCount() { - return getServer().map(server -> server.getOnlinePlayers().size()).orElse(0); + return getServer().map(server -> server.onlinePlayers().size()).orElse(0); } @Override public Collection getPlayerList() { return getServer().map(server -> { - Collection players = server.getOnlinePlayers(); + Collection players = server.onlinePlayers(); List list = new ArrayList<>(players.size()); for (Player player : players) { - list.add(player.getName()); + list.add(player.name()); } return list; }).orElse(Collections.emptyList()); @@ -308,10 +288,10 @@ public Collection getPlayerList() { @Override public Collection getOnlinePlayers() { return getServer().map(server -> { - Collection players = server.getOnlinePlayers(); + Collection players = server.onlinePlayers(); List list = new ArrayList<>(players.size()); for (Player player : players) { - list.add(player.getUniqueId()); + list.add(player.uniqueId()); } return list; }).orElse(Collections.emptyList()); @@ -319,7 +299,7 @@ public Collection getOnlinePlayers() { @Override public boolean isPlayerOnline(UUID uniqueId) { - return getServer().flatMap(server -> server.getPlayer(uniqueId).map(Player::isOnline)).orElse(false); + return getServer().map(server -> server.player(uniqueId).isPresent()).orElse(false); } } 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 90ed9664f..655ed6a7d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java @@ -27,7 +27,6 @@ import me.lucko.luckperms.common.api.LuckPermsApiProvider; import me.lucko.luckperms.common.calculator.CalculatorFactory; -import me.lucko.luckperms.common.command.abstraction.Command; import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter; @@ -38,34 +37,42 @@ import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.manager.track.StandardTrackManager; import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin; +import me.lucko.luckperms.common.sender.AbstractSender; import me.lucko.luckperms.common.sender.DummyConsoleSender; import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.MoreFiles; import me.lucko.luckperms.sponge.calculator.SpongeCalculatorFactory; import me.lucko.luckperms.sponge.commands.SpongeParentCommand; import me.lucko.luckperms.sponge.context.SpongeContextManager; import me.lucko.luckperms.sponge.context.SpongePlayerCalculator; +import me.lucko.luckperms.sponge.listeners.SpongeCommandListUpdater; import me.lucko.luckperms.sponge.listeners.SpongeConnectionListener; import me.lucko.luckperms.sponge.listeners.SpongePlatformListener; import me.lucko.luckperms.sponge.messaging.SpongeMessagingFactory; import me.lucko.luckperms.sponge.model.manager.SpongeGroupManager; import me.lucko.luckperms.sponge.model.manager.SpongeUserManager; import me.lucko.luckperms.sponge.service.LuckPermsService; -import me.lucko.luckperms.sponge.service.ProxyFactory; -import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; -import me.lucko.luckperms.sponge.service.model.ProxiedServiceObject; import me.lucko.luckperms.sponge.service.model.persisted.PersistedCollection; import me.lucko.luckperms.sponge.tasks.ServiceCacheHousekeepingTask; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.luckperms.api.LuckPerms; import net.luckperms.api.query.QueryOptions; -import org.spongepowered.api.service.permission.PermissionDescription; +import org.spongepowered.api.command.Command; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.lifecycle.ProvideServiceEvent; +import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; +import org.spongepowered.api.service.context.ContextService; import org.spongepowered.api.service.permission.PermissionService; +import org.spongepowered.plugin.PluginContainer; -import java.util.Collection; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -88,8 +95,6 @@ public class LPSpongePlugin extends AbstractLuckPermsPlugin { private SpongeContextManager contextManager; private LuckPermsService service; - private boolean lateLoad = false; - public LPSpongePlugin(LPSpongeBootstrap bootstrap) { this.bootstrap = bootstrap; } @@ -107,8 +112,11 @@ protected void setupSenderFactory() { @Override protected Set getGlobalDependencies() { Set dependencies = super.getGlobalDependencies(); - dependencies.add(Dependency.ADVENTURE_PLATFORM); + + //dependencies.add(Dependency.ADVENTURE_PLATFORM); //dependencies.add(Dependency.ADVENTURE_PLATFORM_SPONGEAPI); + dependencies.remove(Dependency.ADVENTURE); + dependencies.add(Dependency.CONFIGURATE_CORE); dependencies.add(Dependency.CONFIGURATE_HOCON); dependencies.add(Dependency.HOCON_CONFIG); @@ -117,14 +125,14 @@ protected Set getGlobalDependencies() { @Override protected ConfigurationAdapter provideConfigurationAdapter() { - return new SpongeConfigAdapter(this, resolveConfig("luckperms.conf")); + return new SpongeConfigAdapter(this, resolveConfig()); } @Override protected void registerPlatformListeners() { this.connectionListener = new SpongeConnectionListener(this); - this.bootstrap.getGame().getEventManager().registerListeners(this.bootstrap, this.connectionListener); - this.bootstrap.getGame().getEventManager().registerListeners(this.bootstrap, new SpongePlatformListener(this)); + this.bootstrap.registerListeners(this.connectionListener); + this.bootstrap.registerListeners(new SpongePlatformListener(this)); } @Override @@ -135,7 +143,22 @@ protected MessagingFactory provideMessagingFactory() { @Override protected void registerCommands() { this.commandManager = new SpongeCommandExecutor(this); - this.bootstrap.getGame().getCommandManager().register(this.bootstrap, this.commandManager, "luckperms", "lp", "perm", "perms", "permission", "permissions"); + this.bootstrap.registerListeners(new RegisterCommandsListener(this.bootstrap.getPluginContainer(), this.commandManager)); + } + + public static final class RegisterCommandsListener { + private final PluginContainer pluginContainer; + private final Command.Raw command; + + RegisterCommandsListener(PluginContainer pluginContainer, Command.Raw command) { + this.pluginContainer = pluginContainer; + this.command = command; + } + + @Listener + public void onCommandRegister(RegisterCommandEvent event) { + event.register(this.pluginContainer, this.command, "luckperms", "lp", "perm", "perms", "permission", "permissions"); + } } @Override @@ -154,36 +177,46 @@ protected CalculatorFactory provideCalculatorFactory() { protected void setupContextManager() { this.contextManager = new SpongeContextManager(this); - SpongePlayerCalculator playerCalculator = new SpongePlayerCalculator(this, getConfiguration().get(ConfigKeys.DISABLED_CONTEXTS)); - this.bootstrap.getGame().getEventManager().registerListeners(this.bootstrap, playerCalculator); + SpongePlayerCalculator playerCalculator = new SpongePlayerCalculator(this); + this.bootstrap.registerListeners(playerCalculator); this.contextManager.registerCalculator(playerCalculator); } @Override protected void setupPlatformHooks() { - getLogger().info("Registering PermissionService..."); this.service = new LuckPermsService(this); - PermissionService oldService = this.bootstrap.getGame().getServiceManager().provide(PermissionService.class).orElse(null); - if (oldService != null && !(oldService instanceof ProxiedServiceObject)) { + //PermissionService oldService = this.bootstrap.getGame().getServiceManager().provide(PermissionService.class).orElse(null); + //if (oldService != null && !(oldService instanceof ProxiedServiceObject)) { + // + // // before registering our permission service, copy any existing permission descriptions + // Collection permissionDescriptions = oldService.getDescriptions(); + // for (PermissionDescription description : permissionDescriptions) { + // if (description instanceof ProxiedServiceObject) { + // continue; + // } + // ProxyFactory.registerDescription(this.service, description); + // } + //} + + this.bootstrap.registerListeners(new RegisterServiceListener(this.service)); + } - // before registering our permission service, copy any existing permission descriptions - Collection permissionDescriptions = oldService.getDescriptions(); - for (PermissionDescription description : permissionDescriptions) { - if (description instanceof ProxiedServiceObject) { - continue; - } - ProxyFactory.registerDescription(this.service, description); - } + public static final class RegisterServiceListener { + private final LuckPermsService service; + + RegisterServiceListener(LuckPermsService service) { + this.service = service; } - if (this.bootstrap.getGame().getPluginManager().getPlugin("permissionsex").isPresent()) { - getLogger().warn("Detected PermissionsEx - assuming it's loaded for migration."); - getLogger().warn("Delaying LuckPerms PermissionService registration."); - this.lateLoad = true; - } else { - this.bootstrap.getGame().getServiceManager().setProvider(this.bootstrap, LPPermissionService.class, this.service); - this.bootstrap.getGame().getServiceManager().setProvider(this.bootstrap, PermissionService.class, this.service.sponge()); + @Listener + public void onPermissionServiceProvide(ProvideServiceEvent.EngineScoped event) { + event.suggest(this.service::sponge); + } + + @Listener + public void onContextServiceProvide(ProvideServiceEvent.EngineScoped event) { + event.suggest(this.service::sponge); } } @@ -194,7 +227,20 @@ protected AbstractEventBus provideEventBus(LuckPermsApiProvider apiProvider) @Override protected void registerApiOnPlatform(LuckPerms api) { - this.bootstrap.getGame().getServiceManager().setProvider(this.bootstrap, LuckPerms.class, api); + this.bootstrap.registerListeners(new RegisterApiListener(api)); + } + + public static final class RegisterApiListener { + private final LuckPerms api; + + RegisterApiListener(LuckPerms api) { + this.api = api; + } + + @Listener + public void onLuckPermsServiceProvide(ProvideServiceEvent event) { + event.suggest(() -> this.api); + } } @Override @@ -209,13 +255,10 @@ protected void performFinalSetup() { for (CommandPermission perm : CommandPermission.values()) { this.service.registerPermissionDescription(perm.getPermission(), null, this.bootstrap.getPluginContainer()); } - } - public void lateEnable() { - if (this.lateLoad) { - getLogger().info("Providing late registration of PermissionService..."); - this.bootstrap.getGame().getServiceManager().setProvider(this.bootstrap, LPPermissionService.class, this.service); - this.bootstrap.getGame().getServiceManager().setProvider(this.bootstrap, PermissionService.class, this.service.sponge()); + // register sponge command list updater + if (getConfiguration().get(ConfigKeys.UPDATE_CLIENT_COMMAND_LIST)) { + getApiProvider().getEventBus().subscribe(new SpongeCommandListUpdater(this)); } } @@ -229,6 +272,22 @@ public void performPlatformDataSync() { this.service.invalidateAllCaches(); } + private Path resolveConfig() { + Path path = this.bootstrap.getConfigDirectory().resolve("luckperms.conf"); + if (!Files.exists(path)) { + try { + MoreFiles.createDirectoriesIfNotExists(this.bootstrap.getConfigDirectory()); + try (InputStream is = getClass().getClassLoader().getResourceAsStream("luckperms.conf")) { + Files.copy(is, path); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return path; + } + @Override public Optional getQueryOptionsForUser(User user) { return this.bootstrap.getPlayer(user.getUniqueId()).map(player -> this.contextManager.getQueryOptions(player)); @@ -238,26 +297,28 @@ public Optional getQueryOptionsForUser(User user) { public Stream getOnlineSenders() { return Stream.concat( Stream.of(getConsoleSender()), - this.bootstrap.getServer().map(server -> server.getOnlinePlayers().stream().map(s -> this.senderFactory.wrap(s))).orElseGet(Stream::empty) + this.bootstrap.getServer().map(server -> server.onlinePlayers().stream().map(s -> this.senderFactory.wrap(s))).orElseGet(Stream::empty) ); } @Override public Sender getConsoleSender() { if (this.bootstrap.getGame().isServerAvailable()) { - return this.senderFactory.wrap(this.bootstrap.getGame().getServer().getConsole()); + return this.senderFactory.wrap(this.bootstrap.getGame().systemSubject()); } else { return new DummyConsoleSender(this) { @Override public void sendMessage(Component message) { - LPSpongePlugin.this.getLogger().info(LegacyComponentSerializer.legacySection().serialize(TranslationManager.render(message))); + for (Component line : AbstractSender.splitNewlines(TranslationManager.render(message))) { + LPSpongePlugin.this.bootstrap.getPluginLogger().info(PlainTextComponentSerializer.plainText().serialize(line)); + } } }; } } @Override - public List> getExtraCommands() { + public List> getExtraCommands() { return Collections.singletonList(new SpongeParentCommand(this)); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeClassPathAppender.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeClassPathAppender.java new file mode 100644 index 000000000..efd521c50 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeClassPathAppender.java @@ -0,0 +1,64 @@ +/* + * 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.sponge; + +import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; +import me.lucko.luckperms.common.plugin.classpath.ReflectionClassPathAppender; + +import java.lang.reflect.Field; +import java.net.URLClassLoader; + +public class SpongeClassPathAppender extends ReflectionClassPathAppender { + + private static URLClassLoader extractClassLoaderFromBootstrap(LuckPermsBootstrap bootstrap) { + ClassLoader classLoader = bootstrap.getClass().getClassLoader(); + + // try to cast directly to URLClassLoader in case things change in the future + if (classLoader instanceof URLClassLoader) { + return (URLClassLoader) classLoader; + } + + Class classLoaderClass = classLoader.getClass(); + + if (!classLoaderClass.getName().equals("cpw.mods.modlauncher.TransformingClassLoader")) { + throw new IllegalStateException("ClassLoader is not instance of TransformingClassLoader: " + classLoaderClass.getName()); + } + + try { + Field delegatedClassLoaderField = classLoaderClass.getDeclaredField("delegatedClassLoader"); + delegatedClassLoaderField.setAccessible(true); + Object delegatedClassLoader = delegatedClassLoaderField.get(classLoader); + return (URLClassLoader) delegatedClassLoader; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + public SpongeClassPathAppender(LuckPermsBootstrap bootstrap) throws IllegalStateException { + super(extractClassLoaderFromBootstrap(bootstrap)); + } + +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCommandExecutor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCommandExecutor.java index 92b04bd60..3467d539b 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCommandExecutor.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCommandExecutor.java @@ -30,23 +30,23 @@ import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.sender.Sender; +import net.kyori.adventure.text.Component; + import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.command.CommandCallable; +import org.spongepowered.api.command.Command; +import org.spongepowered.api.command.CommandCause; +import org.spongepowered.api.command.CommandCompletion; import org.spongepowered.api.command.CommandResult; -import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.command.parameter.ArgumentReader; +import org.spongepowered.api.command.selector.Selector; import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.text.Text; -import org.spongepowered.api.text.selector.Selector; -import org.spongepowered.api.world.Location; -import org.spongepowered.api.world.World; import java.util.List; import java.util.ListIterator; import java.util.Optional; import java.util.stream.Collectors; -public class SpongeCommandExecutor extends CommandManager implements CommandCallable { +public class SpongeCommandExecutor extends CommandManager implements Command.Raw { private final LPSpongePlugin plugin; public SpongeCommandExecutor(LPSpongePlugin plugin) { @@ -55,41 +55,44 @@ public SpongeCommandExecutor(LPSpongePlugin plugin) { } @Override - public @NonNull CommandResult process(@NonNull CommandSource source, @NonNull String args) { - Sender wrapped = this.plugin.getSenderFactory().wrap(source); - List arguments = resolveSelectors(source, ArgumentTokenizer.EXECUTE.tokenizeInput(args)); + public @NonNull CommandResult process(@NonNull CommandCause source, ArgumentReader.@NonNull Mutable args) { + Sender wrapped = this.plugin.getSenderFactory().wrap(source.audience()); + List arguments = resolveSelectors(source, ArgumentTokenizer.EXECUTE.tokenizeInput(args.input())); executeCommand(wrapped, "lp", arguments); return CommandResult.success(); } @Override - public @NonNull List getSuggestions(@NonNull CommandSource source, @NonNull String args, @Nullable Location location) { - Sender wrapped = this.plugin.getSenderFactory().wrap(source); - List arguments = resolveSelectors(source, ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(args)); - return tabCompleteCommand(wrapped, arguments); + public List complete(@NonNull CommandCause source, ArgumentReader.@NonNull Mutable args) { + Sender wrapped = this.plugin.getSenderFactory().wrap(source.audience()); + List arguments = resolveSelectors(source, ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(args.input())); + return tabCompleteCommand(wrapped, arguments) + .stream() + .map(CommandCompletion::of) + .collect(Collectors.toList()); } @Override - public boolean testPermission(@NonNull CommandSource source) { + public boolean canExecute(CommandCause cause) { return true; // we run permission checks internally } @Override - public @NonNull Optional getShortDescription(@NonNull CommandSource source) { - return Optional.of(Text.of("Manage permissions")); + public Optional shortDescription(CommandCause cause) { + return Optional.of(Component.text("Manage permissions")); } @Override - public @NonNull Optional getHelp(@NonNull CommandSource source) { - return Optional.of(Text.of("Run /luckperms to view usage.")); + public Optional extendedDescription(CommandCause cause) { + return Optional.empty(); } @Override - public @NonNull Text getUsage(@NonNull CommandSource source) { - return Text.of("/luckperms"); + public Component usage(CommandCause cause) { + return Component.text("/luckperms"); } - private List resolveSelectors(CommandSource source, List args) { + private List resolveSelectors(CommandCause source, List args) { if (!this.plugin.getConfiguration().get(ConfigKeys.RESOLVE_COMMAND_SELECTORS)) { return args; } @@ -102,7 +105,7 @@ private List resolveSelectors(CommandSource source, List args) { List matchedPlayers; try { - matchedPlayers = Selector.parse(arg).resolve(source).stream() + matchedPlayers = Selector.parse(arg).select(source).stream() .filter(e -> e instanceof Player) .map(e -> (Player) e) .collect(Collectors.toList()); @@ -122,7 +125,7 @@ private List resolveSelectors(CommandSource source, List args) { } Player player = matchedPlayers.get(0); - it.set(player.getUniqueId().toString()); + it.set(player.uniqueId().toString()); } return args; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeEventBus.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeEventBus.java index 55e498d09..105eae51d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeEventBus.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeEventBus.java @@ -29,15 +29,11 @@ import me.lucko.luckperms.common.event.AbstractEventBus; import org.spongepowered.api.Sponge; -import org.spongepowered.api.plugin.PluginContainer; +import org.spongepowered.plugin.PluginContainer; public class SpongeEventBus extends AbstractEventBus { public SpongeEventBus(LPSpongePlugin plugin, LuckPermsApiProvider apiProvider) { super(plugin, apiProvider); - - // register listener - LPSpongeBootstrap bootstrap = plugin.getBootstrap(); - bootstrap.getGame().getEventManager().registerListeners(bootstrap, this); } @Override @@ -46,7 +42,7 @@ protected PluginContainer checkPlugin(Object plugin) throws IllegalArgumentExcep return (PluginContainer) plugin; } - PluginContainer pluginContainer = Sponge.getPluginManager().fromInstance(plugin).orElse(null); + PluginContainer pluginContainer = Sponge.pluginManager().fromInstance(plugin).orElse(null); if (pluginContainer != null) { return pluginContainer; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSchedulerAdapter.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSchedulerAdapter.java index 4d5e5e318..ba73077da 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSchedulerAdapter.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSchedulerAdapter.java @@ -25,34 +25,45 @@ package me.lucko.luckperms.sponge; +import com.google.common.base.Suppliers; + import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask; import me.lucko.luckperms.common.util.Iterators; +import org.spongepowered.api.Game; +import org.spongepowered.api.scheduler.ScheduledTask; import org.spongepowered.api.scheduler.Scheduler; -import org.spongepowered.api.scheduler.SpongeExecutorService; import org.spongepowered.api.scheduler.Task; +import org.spongepowered.api.scheduler.TaskExecutorService; +import org.spongepowered.plugin.PluginContainer; import java.util.Collections; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Supplier; public class SpongeSchedulerAdapter implements SchedulerAdapter { - private final LPSpongeBootstrap bootstrap; - - private final Scheduler scheduler; - private final SpongeExecutorService sync; - private final SpongeExecutorService async; + + private final Game game; + private final PluginContainer pluginContainer; + + private final Scheduler asyncScheduler; + private final Supplier sync; + private final TaskExecutorService async; - private final Set tasks = Collections.newSetFromMap(new WeakHashMap<>()); + private final Set tasks = Collections.newSetFromMap(new WeakHashMap<>()); + + public SpongeSchedulerAdapter(Game game, PluginContainer pluginContainer) { + this.game = game; + this.pluginContainer = pluginContainer; - public SpongeSchedulerAdapter(LPSpongeBootstrap bootstrap, Scheduler scheduler, SpongeExecutorService sync, SpongeExecutorService async) { - this.bootstrap = bootstrap; - this.scheduler = scheduler; - this.sync = sync; - this.async = async; + this.asyncScheduler = game.asyncScheduler(); + this.async = this.asyncScheduler.executor(pluginContainer); + this.sync = Suppliers.memoize(() -> getSyncScheduler().executor(this.pluginContainer)); } @Override @@ -62,47 +73,40 @@ public Executor async() { @Override public Executor sync() { - return this.sync; + return this.sync.get(); } - @Override - public void executeAsync(Runnable runnable) { - this.scheduler.createTaskBuilder().async().execute(runnable).submit(this.bootstrap); + public Scheduler getSyncScheduler() { + return this.game.server().scheduler(); } - @Override - public void executeSync(Runnable runnable) { - this.scheduler.createTaskBuilder().execute(runnable).submit(this.bootstrap); + private SchedulerTask submitAsyncTask(Runnable runnable, Consumer config) { + Task.Builder builder = Task.builder(); + config.accept(builder); + + Task task = builder + .execute(runnable) + .plugin(this.pluginContainer) + .build(); + + ScheduledTask scheduledTask = this.asyncScheduler.submit(task); + this.tasks.add(scheduledTask); + return scheduledTask::cancel; } @Override public SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit) { - Task t = this.scheduler.createTaskBuilder() - .async() - .delay(delay, unit) - .execute(task) - .submit(this.bootstrap); - - this.tasks.add(t); - return t::cancel; + return submitAsyncTask(task, builder -> builder.delay(delay, unit)); } @Override public SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit) { - Task t = this.scheduler.createTaskBuilder() - .async() - .interval(interval, unit) - .delay(interval, unit) - .execute(task) - .submit(this.bootstrap); - - this.tasks.add(t); - return t::cancel; + return submitAsyncTask(task, builder -> builder.delay(interval, unit).interval(interval, unit)); } @Override public void shutdownScheduler() { - Iterators.tryIterate(this.tasks, Task::cancel); + Iterators.tryIterate(this.tasks, ScheduledTask::cancel); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSenderFactory.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSenderFactory.java index 7f7c9a517..06bc24700 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSenderFactory.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSenderFactory.java @@ -30,50 +30,61 @@ import me.lucko.luckperms.common.sender.SenderFactory; import me.lucko.luckperms.sponge.service.CompatibilityUtil; +import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.luckperms.api.util.Tristate; -import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.command.source.ConsoleSource; +import org.spongepowered.api.SystemSubject; +import org.spongepowered.api.command.exception.CommandException; import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.text.Text; -import org.spongepowered.api.text.serializer.TextSerializers; +import org.spongepowered.api.service.permission.Subject; +import java.util.Locale; import java.util.UUID; -public class SpongeSenderFactory extends SenderFactory { +public class SpongeSenderFactory extends SenderFactory { public SpongeSenderFactory(LPSpongePlugin plugin) { super(plugin); } @Override - protected String getName(CommandSource source) { + protected String getName(Audience source) { if (source instanceof Player) { - return source.getName(); + return ((Player) source).name(); } return Sender.CONSOLE_NAME; } @Override - protected UUID getUniqueId(CommandSource source) { + protected UUID getUniqueId(Audience source) { if (source instanceof Player) { - return ((Player) source).getUniqueId(); + return ((Player) source).uniqueId(); } return Sender.CONSOLE_UUID; } @Override - protected void sendMessage(CommandSource source, Component message) { - source.sendMessage(toNativeText(TranslationManager.render(message, source.getLocale()))); + protected void sendMessage(Audience source, Component message) { + Locale locale = null; + if (source instanceof Player) { + locale = ((Player) source).locale(); + } + Component rendered = TranslationManager.render(message, locale); + + source.sendMessage(rendered); } @Override - protected Tristate getPermissionValue(CommandSource source, String node) { - Tristate result = CompatibilityUtil.convertTristate(source.getPermissionValue(source.getActiveContexts(), node)); + protected Tristate getPermissionValue(Audience source, String node) { + if (!(source instanceof Subject)) { + throw new IllegalStateException("Source is not a subject"); + } + + final Subject subject = (Subject) source; + Tristate result = CompatibilityUtil.convertTristate(subject.permissionValue(node)); // check the permdefault - if (result == Tristate.UNDEFINED && source.hasPermission(node)) { + if (result == Tristate.UNDEFINED && subject.hasPermission(node)) { result = Tristate.TRUE; } @@ -81,22 +92,30 @@ protected Tristate getPermissionValue(CommandSource source, String node) { } @Override - protected boolean hasPermission(CommandSource source, String node) { - return source.hasPermission(node); - } + protected boolean hasPermission(Audience source, String node) { + if (!(source instanceof Subject)) { + throw new IllegalStateException("Source is not a subject"); + } - @Override - protected void performCommand(CommandSource source, String command) { - getPlugin().getBootstrap().getGame().getCommandManager().process(source, command); + final Subject subject = (Subject) source; + return subject.hasPermission(node); } @Override - protected boolean isConsole(CommandSource sender) { - return sender instanceof ConsoleSource; - } + protected void performCommand(Audience source, String command) { + if (!(source instanceof Subject)) { + throw new IllegalStateException("Source is not a subject"); + } - public static Text toNativeText(Component component) { - return TextSerializers.JSON.deserialize(GsonComponentSerializer.gson().serialize(component)); + try { + getPlugin().getBootstrap().getGame().server().commandManager().process(((Subject) source), source, command); + } catch (CommandException e) { + // ignore + } } + @Override + protected boolean isConsole(Audience sender) { + return sender instanceof SystemSubject; + } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java index fd21a9e92..2866ce6d3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentAdd.java @@ -39,8 +39,6 @@ import net.luckperms.api.context.ImmutableContextSet; -import org.spongepowered.api.Sponge; - public class ParentAdd extends ChildCommand { public ParentAdd() { super(CommandSpec.SPONGE_PARENT_ADD, "add", CommandPermission.SPONGE_PARENT_ADD, Predicates.inRange(0, 1)); @@ -52,7 +50,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subject String name = args.get(1); ImmutableContextSet contextSet = args.getContextOrEmpty(2); - LPPermissionService service = Sponge.getServiceManager().provideUnchecked(LPPermissionService.class); + LPPermissionService service = subjectData.getParentSubject().getService(); if (service.getLoadedCollections().keySet().stream().map(String::toLowerCase).noneMatch(s -> s.equalsIgnoreCase(collection))) { SpongeCommandUtils.sendPrefixed(sender, "Warning: SubjectCollection '&4" + collection + "&c' doesn't already exist."); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java index f1126f9d1..4c8455fcb 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/ParentRemove.java @@ -39,8 +39,6 @@ import net.luckperms.api.context.ImmutableContextSet; -import org.spongepowered.api.Sponge; - public class ParentRemove extends ChildCommand { public ParentRemove() { super(CommandSpec.SPONGE_PARENT_REMOVE, "remove", CommandPermission.SPONGE_PARENT_REMOVE, Predicates.inRange(0, 1)); @@ -52,7 +50,7 @@ public void execute(LuckPermsPlugin plugin, Sender sender, LPSubjectData subject String name = args.get(1); ImmutableContextSet contextSet = args.getContextOrEmpty(2); - LPPermissionService service = Sponge.getServiceManager().provideUnchecked(LPPermissionService.class); + LPPermissionService service = subjectData.getParentSubject().getService(); if (service.getLoadedCollections().keySet().stream().map(String::toLowerCase).noneMatch(s -> s.equalsIgnoreCase(collection))) { SpongeCommandUtils.sendPrefixed(sender, "Warning: SubjectCollection '&4" + collection + "&c' doesn't exist."); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeCommandUtils.java b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeCommandUtils.java index e976b518d..c6b943038 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeCommandUtils.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/commands/SpongeCommandUtils.java @@ -94,9 +94,9 @@ public static String parentsToString(Iterable parents) { StringBuilder sb = new StringBuilder(); for (LPSubjectReference s : parents) { sb.append("&3> &a") - .append(s.getSubjectIdentifier()) + .append(s.subjectIdentifier()) .append(" &bfrom collection &a") - .append(s.getCollectionIdentifier()) + .append(s.collectionIdentifier()) .append("&b.\n"); } return sb.toString(); 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 25b47dca2..371d96c52 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 @@ -28,33 +28,66 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import me.lucko.luckperms.common.context.manager.ContextManager; -import me.lucko.luckperms.common.context.manager.QueryOptionsCache; +import me.lucko.luckperms.common.context.manager.InlineQueryOptionsSupplier; import me.lucko.luckperms.common.context.manager.QueryOptionsSupplier; import me.lucko.luckperms.common.util.CaffeineFactory; import me.lucko.luckperms.sponge.LPSpongePlugin; +import me.lucko.luckperms.sponge.service.model.ContextCalculatorProxy; +import me.lucko.luckperms.sponge.service.model.TemporaryCauseHolderSubject; +import net.luckperms.api.context.ContextCalculator; +import net.luckperms.api.context.ContextConsumer; import net.luckperms.api.context.ImmutableContextSet; +import net.luckperms.api.context.StaticContextCalculator; import net.luckperms.api.query.QueryOptions; -import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.Cause; import org.spongepowered.api.service.permission.Subject; import java.util.UUID; import java.util.concurrent.TimeUnit; -public class SpongeContextManager extends ContextManager { +public class SpongeContextManager extends ContextManager { - private final LoadingCache> subjectCaches = CaffeineFactory.newBuilder() - .expireAfterAccess(1, TimeUnit.MINUTES) - .build(key -> new QueryOptionsCache<>(key, this)); + private final LoadingCache contextsCache = CaffeineFactory.newBuilder() + .expireAfterWrite(50, TimeUnit.MILLISECONDS) + .build(this::calculate); public SpongeContextManager(LPSpongePlugin plugin) { - super(plugin, Subject.class, Player.class); + super(plugin, Subject.class, ServerPlayer.class); } @Override - public UUID getUniqueId(Player player) { - return player.getUniqueId(); + protected void callContextCalculator(ContextCalculator calculator, Subject subject, ContextConsumer consumer) { + if (subject instanceof TemporaryCauseHolderSubject) { + Cause cause = ((TemporaryCauseHolderSubject) subject).getCause(); + Subject actualSubject = ((TemporaryCauseHolderSubject) subject).getSubject(); + + if (calculator instanceof ContextCalculatorProxy) { + ((ContextCalculatorProxy) calculator).calculate(cause, consumer); + } else if (actualSubject != null) { + calculator.calculate(actualSubject, consumer); + } else if (calculator instanceof StaticContextCalculator) { + ((StaticContextCalculator) calculator).calculate(consumer); + } /* else { + // we just have to fail... + // there's no way to call a LuckPerms ContextCalculator if a Subject instance + // doesn't exist for the cause. + } */ + } else { + Object associatedObject = subject.associatedObject().orElse(null); + if (associatedObject instanceof Subject) { + calculator.calculate((Subject) associatedObject, consumer); + } else { + calculator.calculate(subject, consumer); + } + } + } + + @Override + public UUID getUniqueId(ServerPlayer player) { + return player.uniqueId(); } @Override @@ -63,15 +96,23 @@ public QueryOptionsSupplier getCacheFor(Subject subject) { throw new NullPointerException("subject"); } - return this.subjectCaches.get(subject); + return new InlineQueryOptionsSupplier<>(subject, this.contextsCache); + } + + // override getContext, getQueryOptions and invalidateCache to skip the QueryOptionsSupplier + @Override + public ImmutableContextSet getContext(Subject subject) { + return getQueryOptions(subject).context(); + } + + @Override + public QueryOptions getQueryOptions(Subject subject) { + return this.contextsCache.get(subject); } @Override protected void invalidateCache(Subject subject) { - QueryOptionsCache cache = this.subjectCaches.getIfPresent(subject); - if (cache != null) { - cache.invalidate(); - } + this.contextsCache.invalidate(subject); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java b/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java index 92af5d750..56f255bdf 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java @@ -37,81 +37,57 @@ import net.luckperms.api.context.ImmutableContextSet; import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.CatalogType; -import org.spongepowered.api.CatalogTypes; import org.spongepowered.api.Game; -import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.ValueContainer; import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.entity.living.Humanoid; -import org.spongepowered.api.entity.living.player.gamemode.GameMode; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.Order; -import org.spongepowered.api.event.entity.MoveEntityEvent; -import org.spongepowered.api.event.entity.living.humanoid.ChangeGameModeEvent; +import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; +import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.world.DimensionType; import org.spongepowered.api.world.Locatable; import org.spongepowered.api.world.World; - -import java.util.Set; +import org.spongepowered.api.world.server.ServerWorld; public class SpongePlayerCalculator implements ContextCalculator { private final LPSpongePlugin plugin; - private final boolean gamemode; - private final boolean world; - private final boolean dimensionType; - - public SpongePlayerCalculator(LPSpongePlugin plugin, Set disabled) { + public SpongePlayerCalculator(LPSpongePlugin plugin) { this.plugin = plugin; - this.gamemode = !disabled.contains(DefaultContextKeys.GAMEMODE_KEY); - this.world = !disabled.contains(DefaultContextKeys.WORLD_KEY); - this.dimensionType = !disabled.contains(DefaultContextKeys.DIMENSION_TYPE_KEY); } @Override public void calculate(@NonNull Subject subject, @NonNull ContextConsumer consumer) { - CommandSource source = subject.getCommandSource().orElse(null); - if (source == null) { - return; - } - - if (source instanceof Locatable) { - World world = ((Locatable) source).getWorld(); - if (this.dimensionType) { - consumer.accept(DefaultContextKeys.DIMENSION_TYPE_KEY, getCatalogTypeName(world.getDimension().getType())); - } - if (this.world) { - this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).rewriteAndSubmit(world.getName(), consumer); + if (subject instanceof Locatable) { + World world = ((Locatable) subject).world(); + consumer.accept(DefaultContextKeys.DIMENSION_TYPE_KEY, getContextKey(world.worldType().key(RegistryTypes.WORLD_TYPE))); + if (world instanceof ServerWorld) { + this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).rewriteAndSubmit(getContextKey(((ServerWorld) world).key()), consumer); } } - if (this.gamemode && source instanceof ValueContainer) { - ValueContainer valueContainer = (ValueContainer) source; - valueContainer.get(Keys.GAME_MODE).ifPresent(mode -> consumer.accept(DefaultContextKeys.GAMEMODE_KEY, getCatalogTypeName(mode))); + if (subject instanceof ValueContainer) { + ValueContainer valueContainer = (ValueContainer) subject; + valueContainer.get(Keys.GAME_MODE).ifPresent(mode -> consumer.accept(DefaultContextKeys.GAMEMODE_KEY, getContextKey(mode.key(RegistryTypes.GAME_MODE)))); } } @Override - public @NonNull ContextSet estimatePotentialContexts() { + public ContextSet estimatePotentialContexts() { ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl(); Game game = this.plugin.getBootstrap().getGame(); - if (this.gamemode) { - for (GameMode mode : game.getRegistry().getAllOf(CatalogTypes.GAME_MODE)) { - builder.add(DefaultContextKeys.GAMEMODE_KEY, getCatalogTypeName(mode)); - } - } - if (this.dimensionType) { - for (DimensionType dim : game.getRegistry().getAllOf(CatalogTypes.DIMENSION_TYPE)) { - builder.add(DefaultContextKeys.DIMENSION_TYPE_KEY, getCatalogTypeName(dim)); - } - } - if (this.world && game.isServerAvailable()) { - for (World world : game.getServer().getWorlds()) { - String worldName = world.getName(); + game.registry(RegistryTypes.GAME_MODE).stream().forEach(mode -> { + builder.add(DefaultContextKeys.GAMEMODE_KEY, getContextKey(mode.key(RegistryTypes.GAME_MODE))); + }); + game.registry(RegistryTypes.WORLD_TYPE).stream().forEach(dim -> { + builder.add(DefaultContextKeys.DIMENSION_TYPE_KEY, getContextKey(dim.key(RegistryTypes.WORLD_TYPE))); + }); + if (game.isServerAvailable()) { + for (ServerWorld world : game.server().worldManager().worlds()) { + String worldName = getContextKey(world.key()); if (Context.isValidValue(worldName)) { builder.add(DefaultContextKeys.WORLD_KEY, worldName); } @@ -121,37 +97,29 @@ public void calculate(@NonNull Subject subject, @NonNull ContextConsumer consume return builder.build(); } - private static String getCatalogTypeName(CatalogType type) { - String id = type.getId(); - if (id.startsWith("minecraft:")){ - return id.substring("minecraft:".length()); + private static String getContextKey(ResourceKey key) { + if (key.namespace().equals("minecraft")) { + return key.value(); } - return id; + return key.formatted(); } @Listener(order = Order.LAST) - public void onWorldChange(MoveEntityEvent.Teleport e) { - if (!(this.world || this.dimensionType)) { - return; - } - - Entity targetEntity = e.getTargetEntity(); + public void onWorldChange(ChangeEntityWorldEvent.Post e) { + Entity targetEntity = e.entity(); if (!(targetEntity instanceof Subject)) { return; } - if (e.getFromTransform().getExtent().equals(e.getToTransform().getExtent())) { - return; - } - this.plugin.getContextManager().signalContextUpdate((Subject) targetEntity); } - @Listener(order = Order.LAST) - public void onGameModeChange(ChangeGameModeEvent e) { - Humanoid targetEntity = e.getTargetEntity(); - if (this.gamemode && targetEntity instanceof Subject) { - this.plugin.getContextManager().signalContextUpdate((Subject) targetEntity); - } - } + // TODO: find replacement + //@Listener(order = Order.LAST) + //public void onGameModeChange(ChangeGameModeEvent e) { + // Humanoid targetEntity = e.getHumanoid(); + // if (targetEntity instanceof Subject) { + // this.plugin.getContextManager().signalContextUpdate((Subject) targetEntity); + // } + //} } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeCommandListUpdater.java b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeCommandListUpdater.java new file mode 100644 index 000000000..8f0a28bef --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeCommandListUpdater.java @@ -0,0 +1,108 @@ +/* + * 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.sponge.listeners; + +import com.github.benmanes.caffeine.cache.LoadingCache; + +import me.lucko.luckperms.common.cache.BufferedRequest; +import me.lucko.luckperms.common.event.LuckPermsEventListener; +import me.lucko.luckperms.common.util.CaffeineFactory; +import me.lucko.luckperms.sponge.LPSpongePlugin; + +import net.luckperms.api.event.EventBus; +import net.luckperms.api.event.context.ContextUpdateEvent; +import net.luckperms.api.event.user.UserDataRecalculateEvent; + +import org.spongepowered.api.command.manager.CommandManager; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * Calls {@link CommandManager#updateCommandTreeForPlayer(ServerPlayer)} + * when a players permissions change. + */ +public class SpongeCommandListUpdater implements LuckPermsEventListener { + + private final LPSpongePlugin plugin; + private final LoadingCache sendingBuffers = CaffeineFactory.newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .build(SendBuffer::new); + + public SpongeCommandListUpdater(LPSpongePlugin 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) { + requestUpdate(e.getUser().getUniqueId()); + } + + private void onContextUpdate(ContextUpdateEvent e) { + e.getSubject(ServerPlayer.class).ifPresent(p -> requestUpdate(p.uniqueId())); + } + + private void requestUpdate(UUID uniqueId) { + if (!this.plugin.getBootstrap().isPlayerOnline(uniqueId)) { + return; + } + + // Buffer the request to send a commands update. + this.sendingBuffers.get(uniqueId).request(); + } + + // Called when the buffer times out. + private void sendUpdate(UUID uniqueId) { + this.plugin.getBootstrap().getScheduler().sync().execute(() -> { + ServerPlayer player = this.plugin.getBootstrap().getPlayer(uniqueId).orElse(null); + if (player != null) { + CommandManager commandManager = this.plugin.getBootstrap().getGame().server().commandManager(); + commandManager.updateCommandTreeForPlayer(player); + } + }); + } + + private final class SendBuffer extends BufferedRequest { + private final UUID uniqueId; + + SendBuffer(UUID uniqueId) { + super(500, TimeUnit.MILLISECONDS, SpongeCommandListUpdater.this.plugin.getBootstrap().getScheduler()); + this.uniqueId = uniqueId; + } + + @Override + protected Void perform() { + sendUpdate(this.uniqueId); + return null; + } + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeConnectionListener.java b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeConnectionListener.java index ac6e9fbe6..3a929ed7a 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeConnectionListener.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeConnectionListener.java @@ -31,14 +31,11 @@ import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener; import me.lucko.luckperms.sponge.LPSpongePlugin; -import me.lucko.luckperms.sponge.SpongeSenderFactory; - -import net.kyori.adventure.text.Component; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.Order; import org.spongepowered.api.event.filter.IsCancelled; -import org.spongepowered.api.event.network.ClientConnectionEvent; +import org.spongepowered.api.event.network.ServerSideConnectionEvent; import org.spongepowered.api.profile.GameProfile; import org.spongepowered.api.util.Tristate; @@ -60,22 +57,22 @@ public SpongeConnectionListener(LPSpongePlugin plugin) { @Listener(order = Order.EARLY) @IsCancelled(Tristate.UNDEFINED) - public void onClientAuth(ClientConnectionEvent.Auth e) { + public void onClientAuth(ServerSideConnectionEvent.Auth e) { /* Called when the player first attempts a connection with the server. Listening on AFTER_PRE priority to allow plugins to modify username / UUID data here. (auth plugins) Also, give other plugins a chance to cancel the event. */ - final GameProfile profile = e.getProfile(); - final String username = profile.getName().orElseThrow(() -> new RuntimeException("No username present for user " + profile.getUniqueId())); + final GameProfile profile = e.profile(); + final String username = profile.name().orElseThrow(() -> new RuntimeException("No username present for user " + profile.uniqueId())); if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { - this.plugin.getLogger().info("Processing auth event for " + profile.getUniqueId() + " - " + profile.getName()); + this.plugin.getLogger().info("Processing auth event for " + profile.uniqueId() + " - " + profile.name()); } if (e.isCancelled()) { // another plugin has disallowed the login. - this.plugin.getLogger().info("Another plugin has cancelled the connection for " + profile.getUniqueId() + " - " + username + ". No permissions data will be loaded."); - this.deniedAsyncLogin.add(profile.getUniqueId()); + this.plugin.getLogger().info("Another plugin has cancelled the connection for " + profile.uniqueId() + " - " + username + ". No permissions data will be loaded."); + this.deniedAsyncLogin.add(profile.uniqueId()); return; } @@ -89,34 +86,32 @@ public void onClientAuth(ClientConnectionEvent.Auth e) { - creating a user instance in the UserManager for this connection. - setting up cached data. */ try { - User user = loadUser(profile.getUniqueId(), username); - recordConnection(profile.getUniqueId()); - this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(profile.getUniqueId(), username, user); + User user = loadUser(profile.uniqueId(), username); + recordConnection(profile.uniqueId()); + this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(profile.uniqueId(), username, user); } catch (Exception ex) { - this.plugin.getLogger().severe("Exception occurred whilst loading data for " + profile.getUniqueId() + " - " + profile.getName(), ex); + this.plugin.getLogger().severe("Exception occurred whilst loading data for " + profile.uniqueId() + " - " + profile.name(), ex); - this.deniedAsyncLogin.add(profile.getUniqueId()); + this.deniedAsyncLogin.add(profile.uniqueId()); e.setCancelled(true); - e.setMessageCancelled(false); - Component reason = TranslationManager.render(Message.LOADING_DATABASE_ERROR.build()); - e.setMessage(SpongeSenderFactory.toNativeText(reason)); - this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(profile.getUniqueId(), username, null); + e.setMessage(TranslationManager.render(Message.LOADING_DATABASE_ERROR.build())); + this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(profile.uniqueId(), username, null); } } @Listener(order = Order.LAST) @IsCancelled(Tristate.UNDEFINED) - public void onClientAuthMonitor(ClientConnectionEvent.Auth e) { + public void onClientAuthMonitor(ServerSideConnectionEvent.Auth e) { /* Listen to see if the event was cancelled after we initially handled the connection If the connection was cancelled here, we need to do something to clean up the data that was loaded. */ // Check to see if this connection was denied at LOW. - if (this.deniedAsyncLogin.remove(e.getProfile().getUniqueId())) { + if (this.deniedAsyncLogin.remove(e.profile().uniqueId())) { // This is a problem, as they were denied at low priority, but are now being allowed. if (e.isCancelled()) { - this.plugin.getLogger().severe("Player connection was re-allowed for " + e.getProfile().getUniqueId()); + this.plugin.getLogger().severe("Player connection was re-allowed for " + e.profile().uniqueId()); e.setCancelled(true); } } @@ -124,59 +119,57 @@ public void onClientAuthMonitor(ClientConnectionEvent.Auth e) { @Listener(order = Order.FIRST) @IsCancelled(Tristate.UNDEFINED) - public void onClientLogin(ClientConnectionEvent.Login e) { + public void onClientLogin(ServerSideConnectionEvent.Login e) { /* Called when the player starts logging into the server. At this point, the users data should be present and loaded. Listening on LOW priority to allow plugins to further modify data here. (auth plugins, etc.) */ - final GameProfile profile = e.getProfile(); + final GameProfile profile = e.profile(); if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { - this.plugin.getLogger().info("Processing login event for " + profile.getUniqueId() + " - " + profile.getName()); + this.plugin.getLogger().info("Processing login event for " + profile.uniqueId() + " - " + profile.name()); } - final User user = this.plugin.getUserManager().getIfLoaded(profile.getUniqueId()); + final User user = this.plugin.getUserManager().getIfLoaded(profile.uniqueId()); /* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */ if (user == null) { - this.deniedLogin.add(profile.getUniqueId()); + this.deniedLogin.add(profile.uniqueId()); - if (!getUniqueConnections().contains(profile.getUniqueId())) { - this.plugin.getLogger().warn("User " + profile.getUniqueId() + " - " + profile.getName() + + if (!getUniqueConnections().contains(profile.uniqueId())) { + this.plugin.getLogger().warn("User " + profile.uniqueId() + " - " + profile.name() + " doesn't have data pre-loaded, they have never been processed during pre-login in this session." + " - denying login."); } else { - this.plugin.getLogger().warn("User " + profile.getUniqueId() + " - " + profile.getName() + + this.plugin.getLogger().warn("User " + profile.uniqueId() + " - " + profile.name() + " doesn't currently have data pre-loaded, but they have been processed before in this session." + " - denying login."); } e.setCancelled(true); - e.setMessageCancelled(false); - Component reason = TranslationManager.render(Message.LOADING_STATE_ERROR.build()); - e.setMessage(SpongeSenderFactory.toNativeText(reason)); + e.setMessage(TranslationManager.render(Message.LOADING_STATE_ERROR.build())); } } @Listener(order = Order.LAST) @IsCancelled(Tristate.UNDEFINED) - public void onClientLoginMonitor(ClientConnectionEvent.Login e) { + public void onClientLoginMonitor(ServerSideConnectionEvent.Login e) { /* Listen to see if the event was cancelled after we initially handled the login If the connection was cancelled here, we need to do something to clean up the data that was loaded. */ // Check to see if this connection was denied at LOW. Even if it was denied at LOW, their data will still be present. - if (this.deniedLogin.remove(e.getProfile().getUniqueId())) { + if (this.deniedLogin.remove(e.profile().uniqueId())) { // This is a problem, as they were denied at low priority, but are now being allowed. if (!e.isCancelled()) { - this.plugin.getLogger().severe("Player connection was re-allowed for " + e.getProfile().getUniqueId()); + this.plugin.getLogger().severe("Player connection was re-allowed for " + e.profile().uniqueId()); e.setCancelled(true); } } } @Listener(order = Order.POST) - public void onClientLeave(ClientConnectionEvent.Disconnect e) { - handleDisconnect(e.getTargetEntity().getUniqueId()); + public void onClientLeave(ServerSideConnectionEvent.Disconnect e) { + handleDisconnect(e.player().uniqueId()); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongePlatformListener.java b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongePlatformListener.java index 0ca6d58e5..6184d10ca 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongePlatformListener.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongePlatformListener.java @@ -28,9 +28,9 @@ import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.sponge.LPSpongePlugin; -import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.command.CommandCause; import org.spongepowered.api.event.Listener; -import org.spongepowered.api.event.command.SendCommandEvent; +import org.spongepowered.api.event.command.ExecuteCommandEvent; import java.util.Locale; @@ -42,13 +42,12 @@ public SpongePlatformListener(LPSpongePlugin plugin) { } @Listener - public void onSendCommand(SendCommandEvent e) { - CommandSource source = e.getCause().first(CommandSource.class).orElse(null); - if (source == null) return; + public void onSendCommand(ExecuteCommandEvent e) { + CommandCause source = e.commandCause(); - final String name = e.getCommand().toLowerCase(Locale.ROOT); - if ((name.equals("op") || name.equals("minecraft:op")) && source.hasPermission("minecraft.command.op") || (name.equals("deop") || name.equals("minecraft:deop")) && source.hasPermission("minecraft.command.deop")) { - Message.OP_DISABLED_SPONGE.send(this.plugin.getSenderFactory().wrap(source)); + final String name = e.command().toLowerCase(Locale.ROOT); + if (((name.equals("op") || name.equals("minecraft:op")) && source.hasPermission("minecraft.command.op")) || ((name.equals("deop") || name.equals("minecraft:deop")) && source.hasPermission("minecraft.command.deop"))) { + Message.OP_DISABLED_SPONGE.send(this.plugin.getSenderFactory().wrap(source.audience())); } } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java index ca2707277..609a70d71 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java @@ -34,12 +34,14 @@ import net.luckperms.api.messenger.message.OutgoingMessage; import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.Platform; -import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.network.ChannelBinding; -import org.spongepowered.api.network.ChannelBuf; -import org.spongepowered.api.network.RawDataListener; -import org.spongepowered.api.network.RemoteConnection; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.network.EngineConnectionSide; +import org.spongepowered.api.network.ServerSideConnection; +import org.spongepowered.api.network.channel.ChannelBuf; +import org.spongepowered.api.network.channel.raw.RawDataChannel; +import org.spongepowered.api.network.channel.raw.play.RawPlayDataHandler; +import org.spongepowered.api.scheduler.Task; import java.util.Collection; import java.util.concurrent.TimeUnit; @@ -47,13 +49,13 @@ /** * An implementation of {@link Messenger} using the plugin messaging channels. */ -public class PluginMessageMessenger implements Messenger, RawDataListener { - private static final String CHANNEL = "luckperms:update"; +public class PluginMessageMessenger implements Messenger, RawPlayDataHandler { + private static final ResourceKey CHANNEL = ResourceKey.of("luckperms", "update"); private final LPSpongePlugin plugin; private final IncomingMessageConsumer consumer; - private ChannelBinding.RawDataChannel channel = null; + private RawDataChannel channel = null; public PluginMessageMessenger(LPSpongePlugin plugin, IncomingMessageConsumer consumer) { this.plugin = plugin; @@ -61,37 +63,43 @@ public PluginMessageMessenger(LPSpongePlugin plugin, IncomingMessageConsumer con } public void init() { - this.channel = this.plugin.getBootstrap().getGame().getChannelRegistrar().createRawChannel(this.plugin.getBootstrap(), CHANNEL); - this.channel.addListener(Platform.Type.SERVER, this); + this.channel = this.plugin.getBootstrap().getGame().channelManager().ofType(CHANNEL, RawDataChannel.class); + this.channel.play().addHandler(EngineConnectionSide.SERVER, this); } @Override public void close() { if (this.channel != null) { - this.plugin.getBootstrap().getGame().getChannelRegistrar().unbindChannel(this.channel); + this.channel.play().removeHandler(this); } } @Override public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { - this.plugin.getBootstrap().getSpongeScheduler().createTaskBuilder().interval(10, TimeUnit.SECONDS).execute(task -> { - if (!this.plugin.getBootstrap().getGame().isServerAvailable()) { - return; - } - - Collection players = this.plugin.getBootstrap().getGame().getServer().getOnlinePlayers(); - Player p = Iterables.getFirst(players, null); - if (p == null) { - return; - } - - this.channel.sendTo(p, buf -> buf.writeUTF(outgoingMessage.asEncodedString())); - task.cancel(); - }).submit(this.plugin.getBootstrap()); + Task t = Task.builder() + .interval(10, TimeUnit.SECONDS) + .execute(task -> { + if (!this.plugin.getBootstrap().getGame().isServerAvailable()) { + return; + } + + Collection players = this.plugin.getBootstrap().getGame().server().onlinePlayers(); + ServerPlayer p = Iterables.getFirst(players, null); + if (p == null) { + return; + } + + this.channel.play().sendTo(p, buf -> buf.writeUTF(outgoingMessage.asEncodedString())); + task.cancel(); + }) + .plugin(this.plugin.getBootstrap().getPluginContainer()) + .build(); + + this.plugin.getBootstrap().getScheduler().getSyncScheduler().submit(t); } @Override - public void handlePayload(@NonNull ChannelBuf buf, @NonNull RemoteConnection connection, Platform.@NonNull Type type) { + public void handlePayload(ChannelBuf buf, ServerSideConnection connection) { String msg = buf.readUTF(); this.consumer.consumeIncomingMessageAsString(msg); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java index 735ebe38f..5c9c351b6 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeGroupManager.java @@ -59,7 +59,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; @@ -160,7 +159,7 @@ public CompletableFuture hasRegistered(String identifier) { } @Override - public CompletableFuture> loadSubjects(Set identifiers) { + public CompletableFuture> loadSubjects(Iterable identifiers) { return CompletableFuture.supplyAsync(() -> { ImmutableSet.Builder subjects = ImmutableSet.builder(); for (String id : identifiers) { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeUserManager.java index 330dd9913..10d6aa60f 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeUserManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/manager/SpongeUserManager.java @@ -58,7 +58,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -170,7 +169,7 @@ public CompletableFuture hasRegistered(String identifier) { } @Override - public CompletableFuture> loadSubjects(Set identifiers) { + public CompletableFuture> loadSubjects(Iterable identifiers) { return CompletableFuture.supplyAsync(() -> { ImmutableSet.Builder subjects = ImmutableSet.builder(); for (String id : identifiers) { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java index 8e87e41bf..8a47af6d1 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java @@ -39,19 +39,25 @@ import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; +import me.lucko.luckperms.sponge.service.model.LPSubjectData; import me.lucko.luckperms.sponge.service.model.LPSubjectReference; import me.lucko.luckperms.sponge.service.model.SimplePermissionDescription; +import me.lucko.luckperms.sponge.service.model.SubjectDataUpdateEventImpl; +import me.lucko.luckperms.sponge.service.model.TemporaryCauseHolderSubject; import me.lucko.luckperms.sponge.service.model.persisted.DefaultsCollection; import me.lucko.luckperms.sponge.service.model.persisted.PersistedCollection; import me.lucko.luckperms.sponge.service.model.persisted.SubjectStorage; import me.lucko.luckperms.sponge.service.reference.SubjectReferenceFactory; -import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.plugin.PluginContainer; +import net.kyori.adventure.text.Component; +import net.luckperms.api.context.ImmutableContextSet; + +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.permission.SubjectDataUpdateEvent; import org.spongepowered.api.service.context.ContextCalculator; -import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.text.Text; +import org.spongepowered.plugin.PluginContainer; import java.util.HashMap; import java.util.Locale; @@ -74,7 +80,7 @@ public class LuckPermsService implements LPPermissionService { /** * A cached proxy of this instance */ - private final PermissionService spongeProxy; + private final PermissionAndContextService spongeProxy; /** * Reference factory, used to obtain {@link LPSubjectReference}s. @@ -135,7 +141,7 @@ public LuckPermsService(LPSpongePlugin plugin) { } @Override - public PermissionService sponge() { + public PermissionAndContextService sponge() { return this.spongeProxy; } @@ -145,7 +151,7 @@ public LPSpongePlugin getPlugin() { } @Override - public ContextManager getContextManager() { + public ContextManager getContextManager() { return this.plugin.getContextManager(); } @@ -195,7 +201,7 @@ public ImmutableMap getLoadedCollections() { } @Override - public LPPermissionDescription registerPermissionDescription(String id, Text description, PluginContainer owner) { + public LPPermissionDescription registerPermissionDescription(String id, Component description, PluginContainer owner) { Objects.requireNonNull(id, "id"); SimplePermissionDescription desc = new SimplePermissionDescription(this, id, description, owner); this.permissionDescriptions.put(id, desc); @@ -225,11 +231,30 @@ public ImmutableSet getDescriptions() { } @Override - public void registerContextCalculator(ContextCalculator calculator) { + public void registerContextCalculator(ContextCalculator calculator) { Objects.requireNonNull(calculator); this.plugin.getContextManager().registerCalculator(new ContextCalculatorProxy(calculator)); } + @Override + public ImmutableContextSet getContextsForCause(Cause cause) { + Objects.requireNonNull(cause, "cause"); + return this.plugin.getContextManager().getContext(new TemporaryCauseHolderSubject(cause)); + } + + @Override + public ImmutableContextSet getContextsForCurrentCause() { + return getContextsForCause(this.plugin.getBootstrap().getGame().server().causeStackManager().currentCause()); + } + + @Override + public void fireUpdateEvent(LPSubjectData subjectData) { + this.plugin.getBootstrap().getScheduler().executeAsync(() -> { + SubjectDataUpdateEvent event = new SubjectDataUpdateEventImpl(this.plugin, subjectData); + this.plugin.getBootstrap().getGame().eventManager().post(event); + }); + } + @Override public void invalidateAllCaches() { for (LPSubjectCollection collection : this.collections.values()) { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/ProxyFactory.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/ProxyFactory.java index f72b2aa4b..5bec3ce32 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/ProxyFactory.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/ProxyFactory.java @@ -27,16 +27,19 @@ import me.lucko.luckperms.sponge.service.model.LPPermissionDescription; import me.lucko.luckperms.sponge.service.model.LPPermissionService; +import me.lucko.luckperms.sponge.service.model.LPProxiedSubject; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; import me.lucko.luckperms.sponge.service.model.LPSubjectData; -import me.lucko.luckperms.sponge.service.model.ProxiedSubject; +import me.lucko.luckperms.sponge.service.proxy.api8.PermissionDescriptionProxy; +import me.lucko.luckperms.sponge.service.proxy.api8.PermissionServiceProxy; +import me.lucko.luckperms.sponge.service.proxy.api8.SubjectCollectionProxy; +import me.lucko.luckperms.sponge.service.proxy.api8.SubjectDataProxy; +import me.lucko.luckperms.sponge.service.proxy.api8.SubjectProxy; import net.luckperms.api.model.data.DataType; import org.spongepowered.api.service.permission.PermissionDescription; -import org.spongepowered.api.service.permission.PermissionService; -import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectCollection; import org.spongepowered.api.service.permission.SubjectData; @@ -46,51 +49,25 @@ public final class ProxyFactory { private ProxyFactory() {} - private static final boolean IS_API_7 = isApi7(); - private static boolean isApi7() { - try { - Subject.class.getDeclaredMethod("asSubjectReference"); - return true; - } catch (NoSuchMethodException e) { - return false; - } - } - - public static PermissionService toSponge(LPPermissionService luckPerms) { - return IS_API_7 ? - new me.lucko.luckperms.sponge.service.proxy.api7.PermissionServiceProxy(luckPerms) : - new me.lucko.luckperms.sponge.service.proxy.api6.PermissionServiceProxy(luckPerms); + public static PermissionAndContextService toSponge(LPPermissionService luckPerms) { + return new PermissionServiceProxy(luckPerms); } public static SubjectCollection toSponge(LPSubjectCollection luckPerms) { - return IS_API_7 ? - new me.lucko.luckperms.sponge.service.proxy.api7.SubjectCollectionProxy(luckPerms) : - new me.lucko.luckperms.sponge.service.proxy.api6.SubjectCollectionProxy(luckPerms.getService(), luckPerms); + return new SubjectCollectionProxy(luckPerms); } - public static ProxiedSubject toSponge(LPSubject luckPerms) { - return IS_API_7 ? - new me.lucko.luckperms.sponge.service.proxy.api7.SubjectProxy(luckPerms.getService(), luckPerms.toReference()) : - new me.lucko.luckperms.sponge.service.proxy.api6.SubjectProxy(luckPerms.getService(), luckPerms.toReference()); + public static LPProxiedSubject toSponge(LPSubject luckPerms) { + return new SubjectProxy(luckPerms.getService(), luckPerms.toReference()); } public static SubjectData toSponge(LPSubjectData luckPerms) { LPSubject parentSubject = luckPerms.getParentSubject(); - return IS_API_7 ? - new me.lucko.luckperms.sponge.service.proxy.api7.SubjectDataProxy(parentSubject.getService(), parentSubject.toReference(), luckPerms.getType() == DataType.NORMAL) : - new me.lucko.luckperms.sponge.service.proxy.api6.SubjectDataProxy(parentSubject.getService(), parentSubject.toReference(), luckPerms.getType() == DataType.NORMAL); + return new SubjectDataProxy(parentSubject.getService(), parentSubject.toReference(), luckPerms.getType() == DataType.NORMAL); } public static PermissionDescription toSponge(LPPermissionDescription luckPerms) { - return IS_API_7 ? - new me.lucko.luckperms.sponge.service.proxy.api7.PermissionDescriptionProxy(luckPerms.getService(), luckPerms) : - new me.lucko.luckperms.sponge.service.proxy.api6.PermissionDescriptionProxy(luckPerms.getService(), luckPerms); - } - - public static LPPermissionDescription registerDescription(LPPermissionService service, PermissionDescription description) { - return IS_API_7 ? - me.lucko.luckperms.sponge.service.proxy.api7.DescriptionBuilder.registerDescription(service, description) : - me.lucko.luckperms.sponge.service.proxy.api6.DescriptionBuilder.registerDescription(service, description); + return new PermissionDescriptionProxy(luckPerms.getService(), luckPerms); } } 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 8deb8f9e3..a7d2d089a 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 @@ -30,24 +30,37 @@ import net.luckperms.api.context.ContextConsumer; import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.EventContext; +import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.context.ContextCalculator; import org.spongepowered.api.service.permission.Subject; -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; +import java.util.function.Consumer; public class ContextCalculatorProxy implements ForwardingContextCalculator { - private final ContextCalculator delegate; + private final ContextCalculator delegate; - public ContextCalculatorProxy(ContextCalculator delegate) { + public ContextCalculatorProxy(ContextCalculator delegate) { this.delegate = delegate; } @Override public void calculate(@NonNull Subject subject, @NonNull ContextConsumer consumer) { - this.delegate.accumulateContexts(subject, new ForwardingContextSet(consumer)); + EventContext eventContext = EventContext.builder() + .add(EventContextKeys.SUBJECT, subject) + .build(); + + Cause cause = Cause.builder() + .append(subject) + .build(eventContext); + + calculate(cause, consumer); + } + + public void calculate(@NonNull Cause cause, @NonNull ContextConsumer consumer) { + this.delegate.accumulateContexts(cause, new ForwardingContextConsumer(consumer)); } @Override @@ -55,42 +68,20 @@ public Object delegate() { return this.delegate; } - private static final class ForwardingContextSet implements Set { + private static final class ForwardingContextConsumer implements Consumer { private final ContextConsumer consumer; - private ForwardingContextSet(ContextConsumer consumer) { + private ForwardingContextConsumer(ContextConsumer consumer) { this.consumer = consumer; } @Override - public boolean add(Context context) { - if (!net.luckperms.api.context.Context.isValidKey(context.getKey()) || - !net.luckperms.api.context.Context.isValidValue(context.getValue())) { - return false; + public void accept(Context context) { + if (net.luckperms.api.context.Context.isValidKey(context.getKey()) && + net.luckperms.api.context.Context.isValidValue(context.getValue())) { + this.consumer.accept(context.getKey(), context.getValue()); } - this.consumer.accept(context.getKey(), context.getValue()); - return true; } - - @Override - public boolean addAll(@NonNull Collection c) { - for (Context context : c) { - add(context); - } - return true; - } - - @Override public int size() { throw new UnsupportedOperationException(); } - @Override public boolean isEmpty() { throw new UnsupportedOperationException(); } - @Override public boolean contains(Object o) { throw new UnsupportedOperationException(); } - @Override public @NonNull Iterator iterator() { throw new UnsupportedOperationException(); } - @Override public @NonNull Object[] toArray() { throw new UnsupportedOperationException(); } - @Override public @NonNull T[] toArray(@NonNull T[] a) { throw new UnsupportedOperationException(); } - @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } - @Override public boolean containsAll(@NonNull Collection c) { throw new UnsupportedOperationException(); } - @Override public boolean retainAll(@NonNull Collection c) { throw new UnsupportedOperationException(); } - @Override public boolean removeAll(@NonNull Collection c) { throw new UnsupportedOperationException(); } - @Override public void clear() { throw new UnsupportedOperationException(); } } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/SimplePermissionDescription.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/SimplePermissionDescription.java index a297af14f..a2af9021f 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/SimplePermissionDescription.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/SimplePermissionDescription.java @@ -27,10 +27,11 @@ import me.lucko.luckperms.sponge.service.ProxyFactory; +import net.kyori.adventure.text.Component; + import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.api.service.permission.PermissionDescription; -import org.spongepowered.api.text.Text; +import org.spongepowered.plugin.PluginContainer; import java.util.Map; import java.util.Objects; @@ -41,12 +42,12 @@ public final class SimplePermissionDescription implements LPPermissionDescriptio private final LPPermissionService service; private final String id; - private final @Nullable Text description; + private final @Nullable Component description; private final @Nullable PluginContainer owner; private PermissionDescription spongeProxy = null; - public SimplePermissionDescription(LPPermissionService service, String id, @Nullable Text description, @Nullable PluginContainer owner) { + public SimplePermissionDescription(LPPermissionService service, String id, @Nullable Component description, @Nullable PluginContainer owner) { this.service = service; this.id = Objects.requireNonNull(id, "id"); this.description = description; @@ -72,7 +73,7 @@ public String getId() { } @Override - public Optional getDescription() { + public Optional getDescription() { return Optional.ofNullable(this.description); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/events/LPSubjectDataUpdateEvent.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/SubjectDataUpdateEventImpl.java similarity index 81% rename from sponge/src/main/java/me/lucko/luckperms/sponge/service/events/LPSubjectDataUpdateEvent.java rename to sponge/src/main/java/me/lucko/luckperms/sponge/service/model/SubjectDataUpdateEventImpl.java index b2aaa353c..68f736a26 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/events/LPSubjectDataUpdateEvent.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/SubjectDataUpdateEventImpl.java @@ -23,30 +23,29 @@ * SOFTWARE. */ -package me.lucko.luckperms.sponge.service.events; +package me.lucko.luckperms.sponge.service.model; import me.lucko.luckperms.sponge.LPSpongePlugin; -import me.lucko.luckperms.sponge.service.model.LPSubjectData; import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.event.cause.Cause; -import org.spongepowered.api.event.cause.EventContext; -import org.spongepowered.api.event.cause.EventContextKeys; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.EventContext; +import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.impl.AbstractEvent; import org.spongepowered.api.event.permission.SubjectDataUpdateEvent; import org.spongepowered.api.service.permission.SubjectData; -public class LPSubjectDataUpdateEvent extends AbstractEvent implements SubjectDataUpdateEvent { +public class SubjectDataUpdateEventImpl extends AbstractEvent implements SubjectDataUpdateEvent { private final LPSpongePlugin plugin; private final LPSubjectData subjectData; - public LPSubjectDataUpdateEvent(LPSpongePlugin plugin, LPSubjectData subjectData) { + public SubjectDataUpdateEventImpl(LPSpongePlugin plugin, LPSubjectData subjectData) { this.plugin = plugin; this.subjectData = subjectData; } @Override - public SubjectData getUpdatedData() { + public SubjectData updatedData() { return this.subjectData.sponge(); } @@ -55,7 +54,7 @@ public LPSubjectData getLuckPermsUpdatedData() { } @Override - public @NonNull Cause getCause() { + public @NonNull Cause cause() { EventContext eventContext = EventContext.builder() .add(EventContextKeys.PLUGIN, this.plugin.getBootstrap().getPluginContainer()) .build(); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/TemporaryCauseHolderSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/TemporaryCauseHolderSubject.java new file mode 100644 index 000000000..a84c5f91f --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/TemporaryCauseHolderSubject.java @@ -0,0 +1,108 @@ +/* + * 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.sponge.service.model; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.Subject; +import org.spongepowered.api.service.permission.SubjectCollection; +import org.spongepowered.api.service.permission.SubjectData; +import org.spongepowered.api.service.permission.SubjectReference; +import org.spongepowered.api.util.Tristate; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public class TemporaryCauseHolderSubject implements Subject { + private final @NonNull Cause cause; + private final @Nullable Subject subject; + + public TemporaryCauseHolderSubject(@NonNull Cause cause) { + this.cause = cause; + this.subject = subjectFromCause(this.cause); + } + + private static Subject subjectFromCause(Cause cause) { + Subject subject = cause.context().get(EventContextKeys.SUBJECT).orElse(null); + if (subject != null) { + return subject; + } + + return cause.first(Subject.class).orElse(null); + } + + public @NonNull Cause getCause() { + return this.cause; + } + + public @Nullable Subject getSubject() { + return this.subject; + } + + @Override + public String toString() { + return "CauseSubject(cause=" + this.cause + ')'; + } + + @Override + public boolean equals(Object o) { + return o == this || (o instanceof TemporaryCauseHolderSubject && this.cause.equals(((TemporaryCauseHolderSubject) o).cause)); + } + + @Override + public int hashCode() { + return this.cause.hashCode(); + } + + @Override public SubjectCollection containingCollection() { throw new UnsupportedOperationException(); } + @Override public SubjectReference asSubjectReference() { throw new UnsupportedOperationException(); } + @Override public Optional associatedObject() { throw new UnsupportedOperationException(); } + @Override public Cause contextCause() { throw new UnsupportedOperationException(); } + @Override public boolean isSubjectDataPersisted() { throw new UnsupportedOperationException(); } + @Override public SubjectData subjectData() { throw new UnsupportedOperationException(); } + @Override public SubjectData transientSubjectData() { throw new UnsupportedOperationException(); } + @Override public boolean hasPermission(String permission) { throw new UnsupportedOperationException(); } + @Override public boolean hasPermission(String permission, Cause cause) { throw new UnsupportedOperationException(); } + @Override public boolean hasPermission(String permission, Set contexts) { throw new UnsupportedOperationException(); } + @Override public Tristate permissionValue(String permission) { throw new UnsupportedOperationException(); } + @Override public Tristate permissionValue(String permission, Cause cause) { throw new UnsupportedOperationException(); } + @Override public Tristate permissionValue(String permission, Set contexts) { throw new UnsupportedOperationException(); } + @Override public boolean isChildOf(SubjectReference parent) { throw new UnsupportedOperationException(); } + @Override public boolean isChildOf(SubjectReference parent, Cause cause) { throw new UnsupportedOperationException(); } + @Override public boolean isChildOf(SubjectReference parent, Set contexts) { throw new UnsupportedOperationException(); } + @Override public List parents() { throw new UnsupportedOperationException(); } + @Override public List parents(Cause cause) { throw new UnsupportedOperationException(); } + @Override public List parents(Set contexts) { throw new UnsupportedOperationException(); } + @Override public Optional option(String key) { throw new UnsupportedOperationException(); } + @Override public Optional option(String key, Cause cause) { throw new UnsupportedOperationException(); } + @Override public Optional option(String key, Set contexts) { throw new UnsupportedOperationException(); } + @Override public String identifier() { throw new UnsupportedOperationException(); } + @Override public Optional friendlyIdentifier() { throw new UnsupportedOperationException(); } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java index 00113d9db..d738042bd 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/GroupSubject.java @@ -31,8 +31,6 @@ import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; -import org.spongepowered.api.command.CommandSource; - import java.util.Optional; /** @@ -53,11 +51,6 @@ public Optional getFriendlyIdentifier() { return this.parent.getDisplayName(); } - @Override - public Optional getCommandSource() { - return Optional.empty(); - } - @Override public LPSubjectCollection getParentCollection() { return this.plugin.getService().getGroupSubjects(); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubject.java index 40d3dd50c..98b307876 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubject.java @@ -40,10 +40,9 @@ import me.lucko.luckperms.sponge.model.SpongeGroup; import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.ProxyFactory; -import me.lucko.luckperms.sponge.service.events.UpdateEventHandler; +import me.lucko.luckperms.sponge.service.model.LPProxiedSubject; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectReference; -import me.lucko.luckperms.sponge.service.model.ProxiedSubject; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.model.data.DataType; @@ -64,7 +63,7 @@ public abstract class PermissionHolderSubject implem private final PermissionHolderSubjectData subjectData; private final PermissionHolderSubjectData transientSubjectData; - private ProxiedSubject spongeSubject = null; + private LPProxiedSubject spongeSubject = null; PermissionHolderSubject(LPSpongePlugin plugin, T parent) { this.parent = parent; @@ -74,8 +73,8 @@ public abstract class PermissionHolderSubject implem } public void fireUpdateEvent() { - UpdateEventHandler.fireUpdateEvent(this.plugin, this.subjectData); - UpdateEventHandler.fireUpdateEvent(this.plugin, this.transientSubjectData); + this.plugin.getService().fireUpdateEvent(this.subjectData); + this.plugin.getService().fireUpdateEvent(this.transientSubjectData); } public T getParent() { @@ -83,7 +82,7 @@ public T getParent() { } @Override - public synchronized ProxiedSubject sponge() { + public synchronized LPProxiedSubject sponge() { if (this.spongeSubject == null) { this.spongeSubject = ProxyFactory.toSponge(this); } @@ -123,8 +122,8 @@ public Tristate getPermissionValue(ImmutableContextSet contexts, String permissi @Override public boolean isChildOf(ImmutableContextSet contexts, LPSubjectReference parent) { - return parent.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP) && - getPermissionValue(contexts, Inheritance.key(parent.getSubjectIdentifier())).asBoolean(); + return parent.collectionIdentifier().equals(PermissionService.SUBJECTS_GROUP) && + getPermissionValue(contexts, Inheritance.key(parent.subjectIdentifier())).asBoolean(); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubjectData.java index 39f07e578..f6a19f8e3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/PermissionHolderSubjectData.java @@ -184,11 +184,11 @@ public CompletableFuture addParent(ImmutableContextSet contexts, LPSubj Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(subject, "subject"); - if (!subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) { + if (!subject.collectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) { return CompletableFuture.completedFuture(false); } - Node node = Inheritance.builder(subject.getSubjectIdentifier()) + Node node = Inheritance.builder(subject.subjectIdentifier()) .withContext(contexts) .build(); @@ -204,11 +204,11 @@ public CompletableFuture removeParent(ImmutableContextSet contexts, LPS Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(subject, "subject"); - if (!subject.getCollectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) { + if (!subject.collectionIdentifier().equals(PermissionService.SUBJECTS_GROUP)) { return CompletableFuture.completedFuture(false); } - Node node = Inheritance.builder(subject.getSubjectIdentifier()) + Node node = Inheritance.builder(subject.subjectIdentifier()) .withContext(contexts) .build(); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java index 4706033d2..921c9a394 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/permissionholder/UserSubject.java @@ -30,18 +30,17 @@ import me.lucko.luckperms.sponge.model.SpongeUser; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; +import me.lucko.luckperms.sponge.service.model.LPSubjectUser; import org.spongepowered.api.Sponge; -import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import java.util.Optional; -import java.util.UUID; -import java.util.function.Function; /** * Implements {@link LPSubject} for a {@link SpongeUser}. */ -public final class UserSubject extends PermissionHolderSubject implements LPSubject { +public final class UserSubject extends PermissionHolderSubject implements LPSubject, LPSubjectUser { public UserSubject(LPSpongePlugin plugin, SpongeUser parent) { super(plugin, parent); } @@ -57,9 +56,12 @@ public Optional getFriendlyIdentifier() { } @Override - public Optional getCommandSource() { - final UUID uuid = this.parent.getUniqueId(); - return Sponge.getServer().getPlayer(uuid).map(Function.identity()); + public Optional resolvePlayer() { + if (!Sponge.isServerAvailable()) { + return Optional.empty(); + } + + return Sponge.server().player(this.parent.getUniqueId()); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java index c88252895..859d8b23b 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedCollection.java @@ -49,7 +49,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; @@ -143,7 +142,7 @@ public CompletableFuture hasRegistered(String identifier) { } @Override - public CompletableFuture> loadSubjects(Set identifiers) { + public CompletableFuture> loadSubjects(Iterable identifiers) { ImmutableSet.Builder subjects = ImmutableSet.builder(); for (String id : identifiers) { subjects.add(Objects.requireNonNull(this.subjects.get(id.toLowerCase(Locale.ROOT)))); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java index 9fb681159..bc15b38a2 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/PersistedSubject.java @@ -30,20 +30,15 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.ProxyFactory; -import me.lucko.luckperms.sponge.service.events.UpdateEventHandler; +import me.lucko.luckperms.sponge.service.model.LPProxiedSubject; import me.lucko.luckperms.sponge.service.model.LPSubject; -import me.lucko.luckperms.sponge.service.model.LPSubjectData; -import me.lucko.luckperms.sponge.service.model.ProxiedSubject; import me.lucko.luckperms.sponge.service.model.calculated.CalculatedSubject; import me.lucko.luckperms.sponge.service.model.calculated.CalculatedSubjectData; import me.lucko.luckperms.sponge.service.model.calculated.MonitoredSubjectData; import net.luckperms.api.model.data.DataType; -import org.spongepowered.api.command.CommandSource; - import java.io.IOException; -import java.util.Optional; import java.util.concurrent.TimeUnit; /** @@ -66,7 +61,7 @@ public class PersistedSubject extends CalculatedSubject implements LPSubject { private final PersistedSubjectData subjectData; private final CalculatedSubjectData transientSubjectData; - private ProxiedSubject spongeSubject = null; + private LPProxiedSubject spongeSubject = null; /** * The save buffer instance for saving changes to disk @@ -89,7 +84,7 @@ public PersistedSubject(LuckPermsService service, PersistedCollection parentColl protected void onUpdate(boolean success) { super.onUpdate(success); if (success) { - fireUpdateEvent(this); + PersistedSubject.this.service.fireUpdateEvent(this); } } }; @@ -97,7 +92,7 @@ protected void onUpdate(boolean success) { @Override protected void onUpdate(boolean success) { if (success) { - fireUpdateEvent(this); + PersistedSubject.this.service.fireUpdateEvent(this); } } }; @@ -105,15 +100,6 @@ protected void onUpdate(boolean success) { this.saveBuffer = new SaveBuffer(service.getPlugin()); } - /** - * Calls the subject data update event for the given {@link LPSubjectData} instance. - * - * @param subjectData the subject data - */ - private void fireUpdateEvent(LPSubjectData subjectData) { - UpdateEventHandler.fireUpdateEvent(this.service.getPlugin(), subjectData); - } - /** * Loads data into this {@link PersistedSubject} from the given * {@link SubjectDataContainer} container @@ -149,7 +135,7 @@ void doSave() { } @Override - public ProxiedSubject sponge() { + public LPProxiedSubject sponge() { if (this.spongeSubject == null) { this.spongeSubject = ProxyFactory.toSponge(this); } @@ -181,11 +167,6 @@ public CalculatedSubjectData getTransientSubjectData() { return this.transientSubjectData; } - @Override - public Optional getCommandSource() { - return Optional.empty(); - } - private final class SaveBuffer extends BufferedRequest { public SaveBuffer(LuckPermsPlugin plugin) { super(1, TimeUnit.SECONDS, plugin.getBootstrap().getScheduler()); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectDataContainer.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectDataContainer.java index a26626aac..3d8808c75 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectDataContainer.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/persisted/SubjectDataContainer.java @@ -256,8 +256,8 @@ public JsonObject serialize() { JsonArray data = new JsonArray(); for (LPSubjectReference ref : e.getValue()) { JsonObject parent = new JsonObject(); - parent.addProperty("collection", ref.getCollectionIdentifier()); - parent.addProperty("subject", ref.getSubjectIdentifier()); + parent.addProperty("collection", ref.collectionIdentifier()); + parent.addProperty("subject", ref.subjectIdentifier()); data.add(parent); } section.add("data", data); diff --git a/sponge/src/main/resources/META-INF/sponge_plugins.json b/sponge/src/main/resources/META-INF/sponge_plugins.json new file mode 100644 index 000000000..324073eb2 --- /dev/null +++ b/sponge/src/main/resources/META-INF/sponge_plugins.json @@ -0,0 +1,31 @@ +{ + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "MIT", + "plugins": [ + { + "id": "luckperms", + "name": "LuckPerms", + "version": "${pluginVersion}", + "entrypoint": "me.lucko.luckperms.sponge.LPSpongeBootstrap", + "description": "A permissions plugin", + "links": { + "homepage": "https://luckperms.net" + }, + "contributors": [ + { + "name": "Luck", + "description": "Developer" + } + ], + "dependencies": [ + { + "id": "spongeapi", + "version": "8.0.0" + } + ] + } + ] +} \ No newline at end of file diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 0828a3789..fafe26803 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -619,6 +619,9 @@ disable-bulkupdate = false # - When this happens, the plugin will set their primary group back to default. prevent-primary-group-removal = false +# If LuckPerms should update the list of commands sent to the client when permissions are changed. +update-client-command-list = true + # If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands. # See here for more info: https://minecraft.gamepedia.com/Commands#Target_selectors resolve-command-selectors = false From 65751cd56b938e7ce7e8436fed6eceb18da48ce2 Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 7 Feb 2022 21:25:43 +0000 Subject: [PATCH 062/314] Velocity world context use ServerConnectedEvent (#3245) --- .../luckperms/velocity/context/VelocityPlayerCalculator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java b/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java index 1287a166f..14b0b79f4 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/context/VelocityPlayerCalculator.java @@ -27,7 +27,7 @@ import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.Subscribe; -import com.velocitypowered.api.event.player.ServerPostConnectEvent; +import com.velocitypowered.api.event.player.ServerConnectedEvent; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; @@ -71,7 +71,7 @@ public void calculate(@NonNull Player subject, @NonNull ContextConsumer consumer } @Subscribe(order = PostOrder.FIRST) - public void onServerConnect(ServerPostConnectEvent e) { + public void onServerConnect(ServerConnectedEvent e) { this.plugin.getContextManager().signalContextUpdate(e.getPlayer()); } } From fe80ba6cc443363ed1a8aa0c8633b4509229cd6e Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 7 Feb 2022 21:27:26 +0000 Subject: [PATCH 063/314] Shutdown ForkJoinPool worker on disable (#3247) --- .../common/plugin/scheduler/AbstractJavaScheduler.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/scheduler/AbstractJavaScheduler.java b/common/src/main/java/me/lucko/luckperms/common/plugin/scheduler/AbstractJavaScheduler.java index 2e57b9e11..1c170c836 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/scheduler/AbstractJavaScheduler.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/scheduler/AbstractJavaScheduler.java @@ -96,6 +96,13 @@ public void shutdownExecutor() { } catch (InterruptedException e) { e.printStackTrace(); } + + this.worker.shutdown(); + try { + this.worker.awaitTermination(1, TimeUnit.MINUTES); + } catch (InterruptedException e) { + e.printStackTrace(); + } } private static final class ErrorReportingExecutor implements Executor { From 1b3b9b5c62bab3e98e75ed0ce048356b295c9893 Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 7 Feb 2022 22:35:52 +0000 Subject: [PATCH 064/314] Update commodore to fix running LP commands from a Forge client (#3299) --- bukkit/build.gradle | 2 +- .../me/lucko/luckperms/common/dependencies/Dependency.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 25b2e5096..5e8dacd54 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -17,7 +17,7 @@ dependencies { exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') } - compileOnly 'me.lucko:commodore:1.11' + compileOnly 'me.lucko:commodore:1.12' compileOnly('net.milkbowl.vault:VaultAPI:1.7') { exclude(module: 'bukkit') } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index c83b9c6f1..8af936006 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -128,8 +128,8 @@ public enum Dependency { COMMODORE( "me{}lucko", "commodore", - "1.11", - "TxlkwmLE04ipLfI4HSyC0mzYt/8gTSDBpfCSk+kiaZI=", + "1.12", + "rLpLs20OG9TETCx1QzA+I+LY8P+p6Q9fUjLSmJcl2Kk=", Relocation.of("commodore", "me{}lucko{}commodore") ), COMMODORE_FILE( From bc15e348f5ee2ecc80ef5731f5063e4dfc7e3096 Mon Sep 17 00:00:00 2001 From: Luck Date: Tue, 8 Feb 2022 22:45:00 +0000 Subject: [PATCH 065/314] Optimize bulk user loading for editor command (#3273) --- .../luckperms/common/storage/Storage.java | 10 +++ .../implementation/StorageImplementation.java | 2 + .../file/AbstractConfigurateStorage.java | 11 +++ .../implementation/mongodb/MongoStorage.java | 11 +++ .../implementation/split/SplitStorage.java | 5 ++ .../implementation/sql/SqlStorage.java | 86 +++++++++++++++++-- .../common/webeditor/WebEditorRequest.java | 27 ++++-- 7 files changed, 138 insertions(+), 14 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/Storage.java b/common/src/main/java/me/lucko/luckperms/common/storage/Storage.java index ee4d05c74..61e10fc0e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/Storage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/Storage.java @@ -154,6 +154,16 @@ public CompletableFuture loadUser(UUID uniqueId, String username) { }); } + public CompletableFuture> loadUsers(Set uniqueIds) { + return future(() -> { + Map users = this.implementation.loadUsers(uniqueIds); + for (User user : users.values()) { + this.plugin.getEventDispatcher().dispatchUserLoad(user); + } + return users; + }); + } + public CompletableFuture saveUser(User user) { return future(() -> this.implementation.saveUser(user)); } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/StorageImplementation.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/StorageImplementation.java index 56f85b383..c8e27a046 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/StorageImplementation.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/StorageImplementation.java @@ -69,6 +69,8 @@ default Map getMeta() { User loadUser(UUID uniqueId, String username) throws Exception; + Map loadUsers(Set uniqueIds) throws Exception; + void saveUser(User user) throws Exception; Set getUniqueUsers() throws Exception; diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java index 908dab15c..69a2a1ca3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java @@ -67,6 +67,7 @@ import java.time.Instant; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Optional; @@ -200,6 +201,16 @@ public User loadUser(UUID uniqueId, String username) throws IOException { return user; } + @Override + public Map loadUsers(Set uniqueIds) throws Exception { + // add multithreading here? + Map map = new HashMap<>(); + for (UUID uniqueId : uniqueIds) { + map.put(uniqueId, loadUser(uniqueId, null)); + } + return map; + } + @Override public void saveUser(User user) throws IOException { user.normalData().discardChanges(); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java index 574df62f2..ab384c405 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java @@ -72,6 +72,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -328,6 +329,16 @@ public User loadUser(UUID uniqueId, String username) { return user; } + @Override + public Map loadUsers(Set uniqueIds) throws Exception { + // make this a bulk search? + Map map = new HashMap<>(); + for (UUID uniqueId : uniqueIds) { + map.put(uniqueId, loadUser(uniqueId, null)); + } + return map; + } + @Override public void saveUser(User user) { MongoCollection c = this.database.getCollection(this.prefix + "users"); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/split/SplitStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/split/SplitStorage.java index 81c16779d..5b2584c84 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/split/SplitStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/split/SplitStorage.java @@ -152,6 +152,11 @@ public User loadUser(UUID uniqueId, String username) throws Exception { return implFor(SplitStorageType.USER).loadUser(uniqueId, username); } + @Override + public Map loadUsers(Set uniqueIds) throws Exception { + return implFor(SplitStorageType.USER).loadUsers(uniqueIds); + } + @Override public void saveUser(User user) throws Exception { implFor(SplitStorageType.USER).saveUser(user); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index ee7d653ab..30f839ac4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -84,6 +84,7 @@ public class SqlStorage implements StorageImplementation { private static final Type LIST_STRING_TYPE = new TypeToken>(){}.getType(); private static final String USER_PERMISSIONS_SELECT = "SELECT id, permission, value, server, world, expiry, contexts FROM '{prefix}user_permissions' WHERE uuid=?"; + private static final String USER_PERMISSIONS_SELECT_MULTIPLE = "SELECT uuid, id, permission, value, server, world, expiry, contexts FROM '{prefix}user_permissions' WHERE "; private static final String USER_PERMISSIONS_DELETE_SPECIFIC = "DELETE FROM '{prefix}user_permissions' WHERE id=?"; private static final String USER_PERMISSIONS_DELETE_SPECIFIC_PROPS = "DELETE FROM '{prefix}user_permissions' WHERE uuid=? AND permission=? AND value=? AND server=? AND world=? AND expiry=? AND contexts=?"; private static final String USER_PERMISSIONS_DELETE = "DELETE FROM '{prefix}user_permissions' WHERE uuid=?"; @@ -99,6 +100,7 @@ public class SqlStorage implements StorageImplementation { private static final String PLAYER_SELECT_ALL_UUIDS_BY_USERNAME = "SELECT uuid FROM '{prefix}players' WHERE username=? AND NOT uuid=?"; private static final String PLAYER_DELETE_ALL_UUIDS_BY_USERNAME = "DELETE FROM '{prefix}players' WHERE username=? AND NOT uuid=?"; private static final String PLAYER_SELECT_BY_UUID = "SELECT username, primary_group FROM '{prefix}players' WHERE uuid=? LIMIT 1"; + private static final String PLAYER_SELECT_BY_UUID_MULTIPLE = "SELECT uuid, username, primary_group FROM '{prefix}players' WHERE "; private static final String PLAYER_SELECT_PRIMARY_GROUP_BY_UUID = "SELECT primary_group FROM '{prefix}players' WHERE uuid=? LIMIT 1"; private static final String PLAYER_UPDATE_PRIMARY_GROUP_BY_UUID = "UPDATE '{prefix}players' SET primary_group=? WHERE uuid=?"; @@ -319,16 +321,38 @@ public void applyBulkUpdate(BulkUpdate bulkUpdate) throws SQLException { @Override public User loadUser(UUID uniqueId, String username) throws SQLException { - User user = this.plugin.getUserManager().getOrMake(uniqueId, username); - List nodes; SqlPlayerData playerData; try (Connection c = this.connectionFactory.getConnection()) { - nodes = selectUserPermissions(c, user.getUniqueId()); - playerData = selectPlayerData(c, user.getUniqueId()); + nodes = selectUserPermissions(c, uniqueId); + playerData = selectPlayerData(c, uniqueId); + } + + return createUser(uniqueId, username, playerData, nodes, true); + } + + @Override + public Map loadUsers(Set uniqueIds) throws Exception { + Map> nodesMap; + Map playerDataMap; + + try (Connection c = this.connectionFactory.getConnection()) { + nodesMap = selectUserPermissions(c, uniqueIds); + playerDataMap = selectPlayerData(c, uniqueIds); } + Map users = new HashMap<>(); + for (UUID uniqueId : uniqueIds) { + SqlPlayerData playerData = playerDataMap.get(uniqueId); + List nodes = nodesMap.get(uniqueId); + users.put(uniqueId, createUser(uniqueId, null, playerData, nodes, false)); + } + return users; + } + + private User createUser(UUID uniqueId, String username, SqlPlayerData playerData, List nodes, boolean saveAfterAudit) throws SQLException { + User user = this.plugin.getUserManager().getOrMake(uniqueId, username); if (playerData != null) { if (playerData.primaryGroup != null) { user.getPrimaryGroup().setStoredValue(playerData.primaryGroup); @@ -342,7 +366,7 @@ public User loadUser(UUID uniqueId, String username) throws SQLException { user.loadNodesFromStorage(nodes); this.plugin.getUserManager().giveDefaultIfNeeded(user); - if (user.auditTemporaryNodes()) { + if (user.auditTemporaryNodes() && saveAfterAudit) { saveUser(user); } @@ -866,6 +890,58 @@ private SqlPlayerData selectPlayerData(Connection c, UUID user) throws SQLExcept } } + private Map> selectUserPermissions(Connection c, Set users) throws SQLException { + Map> map = new HashMap<>(); + for (UUID uuid : users) { + map.put(uuid, new ArrayList<>()); + } + + try (Statement s = c.createStatement()) { + String sql = createUserSelectWhereClause(USER_PERMISSIONS_SELECT_MULTIPLE, users); + try (ResultSet rs = s.executeQuery(sql)) { + while (rs.next()) { + UUID uuid = UUID.fromString(rs.getString("uuid")); + Node node = readNode(rs); + if (node != null) { + map.get(uuid).add(readNode(rs)); + } + } + } + } + + return map; + } + + private Map selectPlayerData(Connection c, Set users) throws SQLException { + Map map = new HashMap<>(); + + try (Statement s = c.createStatement()) { + String sql = createUserSelectWhereClause(PLAYER_SELECT_BY_UUID_MULTIPLE, users); + try (ResultSet rs = s.executeQuery(sql)) { + while (rs.next()) { + UUID uuid = UUID.fromString(rs.getString("uuid")); + SqlPlayerData data = new SqlPlayerData( + rs.getString("primary_group"), + rs.getString("username") + ); + map.put(uuid, data); + } + } + } + + return map; + } + + private String createUserSelectWhereClause(String baseQuery, Set users) { + String param = users.stream() + .map(uuid -> "'" + uuid + "'") + .collect(Collectors.joining(",", "uuid IN (", ")")); + + // we don't want to use preparedstatements because the parameter length is variable + // safe to do string concat/replacement because the UUID.toString value isn't injectable + return this.statementProcessor.apply(baseQuery) + param; + } + private void deleteUser(Connection c, UUID user) throws SQLException { try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(USER_PERMISSIONS_DELETE))) { ps.setString(1, user.toString()); diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java index f3982d2ed..b6d4556af 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java @@ -61,9 +61,11 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.GZIPOutputStream; @@ -257,17 +259,24 @@ private static void findMatchingOfflineUsers(Map users, ConstraintNo .distinct(); } - stream.filter(uuid -> !users.containsKey(uuid)) + Set uuids = stream + .filter(uuid -> !users.containsKey(uuid)) .sorted() .limit(MAX_USERS - users.size()) - .map(uuid -> plugin.getStorage().loadUser(uuid, null)) - .forEach(fut -> { - User user = fut.join(); - if (user != null) { - users.put(user.getUniqueId(), user); - plugin.getUserManager().getHouseKeeper().cleanup(user.getUniqueId()); - } - }); + .collect(Collectors.toSet()); + + if (uuids.isEmpty()) { + return; + } + + // load users in bulk from storage + Map loadedUsers = plugin.getStorage().loadUsers(uuids).join(); + users.putAll(loadedUsers); + + // schedule cleanup + for (UUID uniqueId : loadedUsers.keySet()) { + plugin.getUserManager().getHouseKeeper().cleanup(uniqueId); + } } } From 0fe85ed6ff51c72cdc3d01fcce55ea795a38c828 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 9 Feb 2022 19:59:01 +0000 Subject: [PATCH 066/314] Ignore http 503 error from socket server --- .../common/webeditor/WebEditorSession.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java index cb29b1bbd..16611a0f3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java @@ -98,14 +98,23 @@ private void createSocket() { this.socket = socket; this.plugin.getWebEditorStore().sockets().putSocket(this.sender, this.socket); } catch (Exception e) { - if (e instanceof UnsuccessfulRequestException && ((UnsuccessfulRequestException) e).getResponse().code() == 502) { - // 502 - bad gateway, probably means the socket service is offline - // that's ok, no need to send a warning - return; + if (!ignoreSocketConnectError(e)) { + this.plugin.getLogger().warn("Unable to establish socket connection", e); } + } + } - this.plugin.getLogger().warn("Unable to establish socket connection", e); + private static boolean ignoreSocketConnectError(Exception e) { + if (e instanceof UnsuccessfulRequestException) { + UnsuccessfulRequestException req = (UnsuccessfulRequestException) e; + int code = req.getResponse().code(); + + // 502 - bad gateway / 503 - service unavailable + // probably means the socket service is offline, that's ok, no need to send a warning + return code == 502 || code == 503; } + + return false; } private void createInitialSession() { From b13a74c61edba0968831f8359b1ff73d82f5c199 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 9 Feb 2022 20:08:16 +0000 Subject: [PATCH 067/314] Pre-generate web editor keypair to speed up initial session init --- .../common/webeditor/socket/WebEditorSocket.java | 10 +++++----- .../common/webeditor/store/WebEditorStore.java | 13 +++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java index aaed889e4..74c82f17a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java @@ -58,11 +58,11 @@ public class WebEditorSocket { private final WebEditorSession session; /** The socket listener that handles incoming messages */ private final WebEditorSocketListener listener; + /** The public and private keys used to sign messages sent by the plugin */ + private final KeyPair pluginKeyPair; /** The websocket backing the connection */ private BytesocksClient.Socket socket; - /** The public and private keys used to sign messages sent by the plugin */ - private KeyPair localKeys; /** A task to check if the socket is still active */ private SchedulerTask keepaliveTask; /** The public key used by the editor to sign messages */ @@ -75,6 +75,7 @@ public WebEditorSocket(LuckPermsPlugin plugin, Sender sender, WebEditorSession s this.sender = sender; this.session = session; this.listener = new WebEditorSocketListener(this); + this.pluginKeyPair = plugin.getWebEditorStore().keyPair(); } /** @@ -86,7 +87,6 @@ public WebEditorSocket(LuckPermsPlugin plugin, Sender sender, WebEditorSession s */ public void initialize(BytesocksClient client) throws UnsuccessfulRequestException, IOException { this.socket = client.createSocket(this.listener); - this.localKeys = CryptographyUtils.generateKeyPair(); } /** @@ -112,7 +112,7 @@ public void waitForConnect(long timeout, TimeUnit unit) { */ public void appendDetailToRequest(WebEditorRequest request) { String channelId = this.socket.channelId(); - String publicKey = Base64.getEncoder().encodeToString(this.localKeys.getPublic().getEncoded()); + String publicKey = Base64.getEncoder().encodeToString(this.pluginKeyPair.getPublic().getEncoded()); JsonObject socket = new JsonObject(); socket.addProperty("protocolVersion", PROTOCOL_VERSION); @@ -133,7 +133,7 @@ public void appendDetailToRequest(WebEditorRequest request) { */ public void send(JsonObject msg) { String encoded = GsonProvider.normal().toJson(msg); - String signature = CryptographyUtils.sign(this.localKeys.getPrivate(), encoded); + String signature = CryptographyUtils.sign(this.pluginKeyPair.getPrivate(), encoded); JsonObject frame = new JObject() .add("msg", encoded) diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java index 82a7bd47a..26d28e846 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorStore.java @@ -26,6 +26,10 @@ package me.lucko.luckperms.common.webeditor.store; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.webeditor.socket.CryptographyUtils; + +import java.security.KeyPair; +import java.util.concurrent.CompletableFuture; /** * Contains a store of known web editor sessions and provides a lookup function for @@ -35,11 +39,13 @@ public class WebEditorStore { private final WebEditorSessionMap sessions; private final WebEditorSocketMap sockets; private final WebEditorKeystore keystore; + private final CompletableFuture keyPair; public WebEditorStore(LuckPermsPlugin plugin) { this.sessions = new WebEditorSessionMap(); this.sockets = new WebEditorSocketMap(); this.keystore = new WebEditorKeystore(plugin.getBootstrap().getConfigDirectory().resolve("editor-keystore.json")); + this.keyPair = CompletableFuture.supplyAsync(CryptographyUtils::generateKeyPair, plugin.getBootstrap().getScheduler().async()); } public WebEditorSessionMap sessions() { @@ -54,4 +60,11 @@ public WebEditorKeystore keystore() { return this.keystore; } + public KeyPair keyPair() { + if (!this.keyPair.isDone()) { + throw new IllegalStateException("Web editor keypair has not been generated yet! Has the server just started?"); + } + return this.keyPair.join(); + } + } From d45d9a295bd0bb0bf0855d54a143c2e2d01a39a4 Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Wed, 9 Feb 2022 15:15:18 -0500 Subject: [PATCH 068/314] Optimize parent resolution in info commands (#3309) --- .../common/commands/group/GroupInfo.java | 20 +++++++++---------- .../common/commands/user/UserInfo.java | 20 +++++++++---------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java index 213fd587e..d3f2d3598 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java @@ -42,6 +42,7 @@ import net.luckperms.api.node.types.InheritanceNode; import net.luckperms.api.query.QueryOptions; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -60,26 +61,23 @@ public void execute(LuckPermsPlugin plugin, Sender sender, Group target, Argumen Message.GROUP_INFO_GENERAL.send(sender, target.getName(), target.getPlainDisplayName(), target.getWeight()); - List parents = target.normalData().inheritanceAsSortedSet().stream() + Map> parents = target.normalData().inheritanceAsSortedSet().stream() .filter(Node::getValue) - .filter(n -> !n.hasExpiry()) - .collect(Collectors.toList()); + .collect(Collectors.groupingBy(Node::hasExpiry, Collectors.toList())); - List tempParents = target.normalData().inheritanceAsSortedSet().stream() - .filter(Node::getValue) - .filter(Node::hasExpiry) - .collect(Collectors.toList()); + List temporaryParents = parents.getOrDefault(true, Collections.emptyList()); + List permanentParents = parents.getOrDefault(false, Collections.emptyList()); - if (!parents.isEmpty()) { + if (!permanentParents.isEmpty()) { Message.INFO_PARENT_HEADER.send(sender); - for (InheritanceNode node : parents) { + for (InheritanceNode node : permanentParents) { Message.INFO_PARENT_NODE_ENTRY.send(sender, node); } } - if (!tempParents.isEmpty()) { + if (!temporaryParents.isEmpty()) { Message.INFO_TEMP_PARENT_HEADER.send(sender); - for (InheritanceNode node : tempParents) { + for (InheritanceNode node : temporaryParents) { Message.INFO_PARENT_TEMPORARY_NODE_ENTRY.send(sender, node); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java index 5b20cf8ae..068f4e6b7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java @@ -44,6 +44,7 @@ import net.luckperms.api.node.types.InheritanceNode; import net.luckperms.api.query.QueryOptions; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -67,26 +68,23 @@ public void execute(LuckPermsPlugin plugin, Sender sender, User target, Argument plugin.getBootstrap().isPlayerOnline(target.getUniqueId()) ); - List parents = target.normalData().inheritanceAsSortedSet().stream() + Map> parents = target.normalData().inheritanceAsSortedSet().stream() .filter(Node::getValue) - .filter(n -> !n.hasExpiry()) - .collect(Collectors.toList()); + .collect(Collectors.groupingBy(Node::hasExpiry, Collectors.toList())); - List tempParents = target.normalData().inheritanceAsSortedSet().stream() - .filter(Node::getValue) - .filter(Node::hasExpiry) - .collect(Collectors.toList()); + List temporaryParents = parents.getOrDefault(true, Collections.emptyList()); + List permanentParents = parents.getOrDefault(false, Collections.emptyList()); - if (!parents.isEmpty()) { + if (!permanentParents.isEmpty()) { Message.INFO_PARENT_HEADER.send(sender); - for (InheritanceNode node : parents) { + for (InheritanceNode node : permanentParents) { Message.INFO_PARENT_NODE_ENTRY.send(sender, node); } } - if (!tempParents.isEmpty()) { + if (!temporaryParents.isEmpty()) { Message.INFO_TEMP_PARENT_HEADER.send(sender); - for (InheritanceNode node : tempParents) { + for (InheritanceNode node : temporaryParents) { Message.INFO_PARENT_TEMPORARY_NODE_ENTRY.send(sender, node); } } From 59f3866e3625842c26f2eafdc5c00871570a43c0 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 9 Feb 2022 21:08:18 +0000 Subject: [PATCH 069/314] Warn if the API was classloaded by another plugin --- .../luckperms/bukkit/LPBukkitBootstrap.java | 4 +- .../luckperms/bungee/LPBungeeBootstrap.java | 4 +- .../common/api/LuckPermsApiProvider.java | 35 ++++++++++++++++ .../plugin/AbstractLuckPermsPlugin.java | 1 + .../bootstrap/BootstrappedWithLoader.java | 40 +++++++++++++++++++ .../luckperms/nukkit/LPNukkitBootstrap.java | 4 +- 6 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/BootstrappedWithLoader.java diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitBootstrap.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitBootstrap.java index fd9333f76..949af47bc 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitBootstrap.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitBootstrap.java @@ -27,6 +27,7 @@ import me.lucko.luckperms.bukkit.util.NullSafeConsoleCommandSender; import me.lucko.luckperms.common.loader.LoaderBootstrap; +import me.lucko.luckperms.common.plugin.bootstrap.BootstrappedWithLoader; import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; import me.lucko.luckperms.common.plugin.classpath.ClassPathAppender; import me.lucko.luckperms.common.plugin.classpath.JarInJarClassPathAppender; @@ -56,7 +57,7 @@ /** * Bootstrap plugin for LuckPerms running on Bukkit. */ -public class LPBukkitBootstrap implements LuckPermsBootstrap, LoaderBootstrap { +public class LPBukkitBootstrap implements LuckPermsBootstrap, LoaderBootstrap, BootstrappedWithLoader { private final JavaPlugin loader; /** @@ -111,6 +112,7 @@ public LPBukkitBootstrap(JavaPlugin loader) { // provide adapters + @Override public JavaPlugin getLoader() { return this.loader; } diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeeBootstrap.java b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeeBootstrap.java index 3174c80a0..5caccd210 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeeBootstrap.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeeBootstrap.java @@ -27,6 +27,7 @@ import me.lucko.luckperms.bungee.util.RedisBungeeUtil; import me.lucko.luckperms.common.loader.LoaderBootstrap; +import me.lucko.luckperms.common.plugin.bootstrap.BootstrappedWithLoader; import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; import me.lucko.luckperms.common.plugin.classpath.ClassPathAppender; import me.lucko.luckperms.common.plugin.classpath.JarInJarClassPathAppender; @@ -56,7 +57,7 @@ /** * Bootstrap plugin for LuckPerms running on BungeeCord. */ -public class LPBungeeBootstrap implements LuckPermsBootstrap, LoaderBootstrap { +public class LPBungeeBootstrap implements LuckPermsBootstrap, LoaderBootstrap, BootstrappedWithLoader { private final Plugin loader; /** @@ -101,6 +102,7 @@ public LPBungeeBootstrap(Plugin loader) { // provide adapters + @Override public Plugin getLoader() { return this.loader; } diff --git a/common/src/main/java/me/lucko/luckperms/common/api/LuckPermsApiProvider.java b/common/src/main/java/me/lucko/luckperms/common/api/LuckPermsApiProvider.java index 77aecbc4f..143df9e4f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/LuckPermsApiProvider.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/LuckPermsApiProvider.java @@ -41,8 +41,12 @@ import me.lucko.luckperms.common.event.AbstractEventBus; import me.lucko.luckperms.common.messaging.LuckPermsMessagingService; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.plugin.bootstrap.BootstrappedWithLoader; +import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; +import me.lucko.luckperms.common.plugin.logging.PluginLogger; import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; import net.luckperms.api.actionlog.ActionLogger; import net.luckperms.api.context.ContextManager; import net.luckperms.api.messaging.MessagingService; @@ -93,6 +97,37 @@ public LuckPermsApiProvider(LuckPermsPlugin plugin) { this.metaStackFactory = new ApiMetaStackFactory(plugin); } + public void ensureApiWasLoadedByPlugin() { + LuckPermsBootstrap bootstrap = this.plugin.getBootstrap(); + ClassLoader pluginClassLoader; + if (bootstrap instanceof BootstrappedWithLoader) { + pluginClassLoader = ((BootstrappedWithLoader) bootstrap).getLoader().getClass().getClassLoader(); + } else { + pluginClassLoader = bootstrap.getClass().getClassLoader(); + } + + for (Class apiClass : new Class[]{LuckPerms.class, LuckPermsProvider.class}) { + ClassLoader apiClassLoader = apiClass.getClassLoader(); + + if (!apiClassLoader.equals(pluginClassLoader)) { + String guilty = "unknown"; + try { + guilty = bootstrap.identifyClassLoader(apiClassLoader); + } catch (Exception e) { + // ignore + } + + PluginLogger logger = this.plugin.getLogger(); + logger.warn("It seems that the LuckPerms API has been (class)loaded by a plugin other than LuckPerms!"); + logger.warn("The API was loaded by " + apiClassLoader + " (" + guilty + ") and the " + + "LuckPerms plugin was loaded by " + pluginClassLoader.toString() + "."); + logger.warn("This indicates that the other plugin has incorrectly \"shaded\" the " + + "LuckPerms API into its jar file. This can cause errors at runtime and should be fixed."); + return; + } + } + } + @Override public @NonNull String getServerName() { return this.plugin.getConfiguration().get(ConfigKeys.SERVER); diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 4836b9e76..f6f2fd806 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -194,6 +194,7 @@ public final void enable() { // register with the LP API this.apiProvider = new LuckPermsApiProvider(this); + this.apiProvider.ensureApiWasLoadedByPlugin(); this.eventDispatcher = new EventDispatcher(provideEventBus(this.apiProvider)); getBootstrap().getScheduler().executeAsync(GeneratedEventClass::preGenerate); ApiRegistrationUtil.registerProvider(this.apiProvider); diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/BootstrappedWithLoader.java b/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/BootstrappedWithLoader.java new file mode 100644 index 000000000..d5cdb6f65 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/BootstrappedWithLoader.java @@ -0,0 +1,40 @@ +/* + * 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.plugin.bootstrap; + +/** + * A {@link LuckPermsBootstrap} that was bootstrapped by a loader. + */ +public interface BootstrappedWithLoader { + + /** + * Gets the loader object that did the bootstrapping. + * + * @return the loader + */ + Object getLoader(); + +} diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitBootstrap.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitBootstrap.java index 497f1d020..8dc90ad22 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitBootstrap.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitBootstrap.java @@ -26,6 +26,7 @@ package me.lucko.luckperms.nukkit; import me.lucko.luckperms.common.loader.LoaderBootstrap; +import me.lucko.luckperms.common.plugin.bootstrap.BootstrappedWithLoader; import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; import me.lucko.luckperms.common.plugin.classpath.ClassPathAppender; import me.lucko.luckperms.common.plugin.classpath.JarInJarClassPathAppender; @@ -49,7 +50,7 @@ /** * Bootstrap plugin for LuckPerms running on Nukkit. */ -public class LPNukkitBootstrap implements LuckPermsBootstrap, LoaderBootstrap { +public class LPNukkitBootstrap implements LuckPermsBootstrap, LoaderBootstrap, BootstrappedWithLoader { private final PluginBase loader; /** @@ -91,6 +92,7 @@ public LPNukkitBootstrap(PluginBase loader) { // provide adapters + @Override public PluginBase getLoader() { return this.loader; } From 99c3d68d3a618677800fd674363855ae143f4452 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 9 Feb 2022 21:39:45 +0000 Subject: [PATCH 070/314] API 5.4 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 8fc9ce88d..4a17698b8 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ subprojects { apply plugin: 'org.cadixdev.licenser' group = 'me.lucko.luckperms' - version = '5.3-SNAPSHOT' + version = '5.4-SNAPSHOT' sourceCompatibility = 1.8 targetCompatibility = 1.8 @@ -39,7 +39,7 @@ subprojects { } project.ext.majorVersion = '5' - project.ext.minorVersion = '3' + project.ext.minorVersion = '4' project.ext.patchVersion = determinePatchVersion() project.ext.apiVersion = project.ext.majorVersion + '.' + project.ext.minorVersion project.ext.fullVersion = project.ext.apiVersion + '.' + project.ext.patchVersion From 3eca6205abe00d71416f6091b5ab312bb03fe260 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 9 Feb 2022 22:15:09 +0000 Subject: [PATCH 071/314] Fix API buildscript --- api/build.gradle | 55 ++++++++++++++++++--------------------- api/javadoc/overview.html | 3 ++- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/api/build.gradle b/api/build.gradle index 564464559..d3a46f9e5 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -1,9 +1,9 @@ group = 'net.luckperms' -project.version = '5.3' +project.version = '5.4' dependencies { - compileOnly 'org.checkerframework:checker-qual:3.12.0' - compileOnly 'org.jetbrains:annotations:20.1.0' + compileOnly 'org.checkerframework:checker-qual:3.21.2' + compileOnly 'org.jetbrains:annotations:23.0.0' } // Only used occasionally for deployment - not needed for normal builds. @@ -18,12 +18,12 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass options.charSet = 'UTF-8' options.links( 'https://checkerframework.org/api/', - 'https://javadoc.io/static/org.jetbrains/annotations/20.1.0/' + 'https://javadoc.io/static/org.jetbrains/annotations/23.0.0/' ) options.addStringOption('Xdoclint:none', '-quiet') + options.addStringOption('-since', '5.0,5.1,5.2,5.3,5.4') if (JavaVersion.current() > JavaVersion.VERSION_1_8) { - options.addBooleanOption('-no-module-directories', true) options.links.add('https://docs.oracle.com/en/java/javase/11/docs/api/') } else { options.links.add('https://docs.oracle.com/javase/8/docs/api/') @@ -45,11 +45,6 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass archives sourcesJar } - signing { - useGpgCmd() - sign configurations.archives - } - publishing { repositories { maven { @@ -73,44 +68,46 @@ if (project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePass artifact javadocJar pom { - name 'LuckPerms API' - description 'A permissions plugin for Minecraft servers.' - url 'https://luckperms.net' + name = 'LuckPerms API' + description = 'A permissions plugin for Minecraft servers.' + url = 'https://luckperms.net' licenses { license { - name 'MIT' - url 'https://opensource.org/licenses/MIT' + name = 'MIT' + url = 'https://opensource.org/licenses/MIT' } } developers { developer { - id 'lucko' - name 'Luck' - url 'https://lucko.me' - email 'git@lucko.me' + id = 'lucko' + name = 'Luck' + url = 'https://lucko.me' + email = 'git@lucko.me' } } scm { - connection 'scm:git:https://github.com/lucko/LuckPerms.git' - developerConnection 'scm:git:git@github.com:lucko/LuckPerms.git' - url 'https://github.com/lucko/LuckPerms' - } - - ciManagement { - system 'Jenkins' - url 'https://ci.lucko.me/job/LuckPerms' + connection = 'scm:git:https://github.com/LuckPerms/LuckPerms.git' + developerConnection = 'scm:git:git@github.com:LuckPerms/LuckPerms.git' + url = 'https://github.com/LuckPerms/LuckPerms' } issueManagement { - system 'GitHub' - url 'https://github.com/lucko/LuckPerms/issues' + system = 'GitHub' + url = 'https://github.com/LuckPerms/LuckPerms/issues' } } } } } + + signing { + useGpgCmd() + sign configurations.archives + sign publishing.publications.mavenJava + required = true + } } */ diff --git a/api/javadoc/overview.html b/api/javadoc/overview.html index 061a300b6..7548e4eda 100644 --- a/api/javadoc/overview.html +++ b/api/javadoc/overview.html @@ -8,10 +8,11 @@

Useful Links

  • Wiki - API Introduction
  • Wiki - API Usage
  • +
  • Source Code
  • Maven

    - The API artifact is deployed to Maven Central under + The API artifact is deployed to Maven Central with group id: net.luckperms, artifact id: api. From 86d2523d33f09cc3ffc9a03e1ea67daba219ba0c Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 11 Feb 2022 23:25:31 +0000 Subject: [PATCH 072/314] Fix SpongePlayerCalculator errors --- .../sponge/context/SpongePlayerCalculator.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java b/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java index 56f255bdf..993021521 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/context/SpongePlayerCalculator.java @@ -39,6 +39,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.Game; import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Server; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.ValueContainer; import org.spongepowered.api.entity.Entity; @@ -82,11 +83,15 @@ public ContextSet estimatePotentialContexts() { game.registry(RegistryTypes.GAME_MODE).stream().forEach(mode -> { builder.add(DefaultContextKeys.GAMEMODE_KEY, getContextKey(mode.key(RegistryTypes.GAME_MODE))); }); - game.registry(RegistryTypes.WORLD_TYPE).stream().forEach(dim -> { - builder.add(DefaultContextKeys.DIMENSION_TYPE_KEY, getContextKey(dim.key(RegistryTypes.WORLD_TYPE))); - }); + if (game.isServerAvailable()) { - for (ServerWorld world : game.server().worldManager().worlds()) { + Server server = game.server(); + + server.registry(RegistryTypes.WORLD_TYPE).stream().forEach(dim -> { + builder.add(DefaultContextKeys.DIMENSION_TYPE_KEY, getContextKey(dim.key(RegistryTypes.WORLD_TYPE))); + }); + + for (ServerWorld world : server.worldManager().worlds()) { String worldName = getContextKey(world.key()); if (Context.isValidValue(worldName)) { builder.add(DefaultContextKeys.WORLD_KEY, worldName); From 5c8b0a604e4300e485c3ad2d5368d2da10d687ff Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 16 Feb 2022 19:53:33 +0000 Subject: [PATCH 073/314] Maintain order in editor users/groups collection --- .../lucko/luckperms/common/webeditor/WebEditorSession.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java index 16611a0f3..137558518 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorSession.java @@ -39,7 +39,7 @@ import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; import java.io.IOException; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -79,8 +79,8 @@ public WebEditorSession(WebEditorRequest initialRequest, LuckPermsPlugin plugin, this.sender = sender; this.cmdLabel = cmdLabel; - this.holders = new HashSet<>(initialRequest.getHolders().keySet()); - this.tracks = new HashSet<>(initialRequest.getTracks().keySet()); + this.holders = new LinkedHashSet<>(initialRequest.getHolders().keySet()); + this.tracks = new LinkedHashSet<>(initialRequest.getTracks().keySet()); } public void open() { From b2352346338ccaee052d317d73557f1dce10a2c3 Mon Sep 17 00:00:00 2001 From: lucko Date: Thu, 17 Feb 2022 00:22:46 +0000 Subject: [PATCH 074/314] Sponge use jar-in-jar loader --- settings.gradle | 5 +- sponge/build.gradle | 4 +- sponge/loader/build.gradle | 32 ++++++++ .../sponge/loader/SpongeLoaderPlugin.java | 75 +++++++++++++++++++ .../resources/META-INF/sponge_plugins.json | 2 +- .../luckperms/sponge/LPSpongeBootstrap.java | 51 ++++++++----- .../sponge/SpongeClassPathAppender.java | 64 ---------------- 7 files changed, 145 insertions(+), 88 deletions(-) create mode 100644 sponge/loader/build.gradle create mode 100644 sponge/loader/src/main/java/me/lucko/luckperms/sponge/loader/SpongeLoaderPlugin.java rename sponge/{ => loader}/src/main/resources/META-INF/sponge_plugins.json (88%) delete mode 100644 sponge/src/main/java/me/lucko/luckperms/sponge/SpongeClassPathAppender.java diff --git a/settings.gradle b/settings.gradle index db3bdea62..17a0c9c99 100644 --- a/settings.gradle +++ b/settings.gradle @@ -23,6 +23,9 @@ include ( 'fabric', 'nukkit', 'nukkit:loader', - 'sponge', 'sponge:sponge-service', 'sponge:sponge-service-api8', + 'sponge', + 'sponge:loader', + 'sponge:sponge-service', + 'sponge:sponge-service-api8', 'velocity' ) diff --git a/sponge/build.gradle b/sponge/build.gradle index 275da4a33..5a26cb16d 100644 --- a/sponge/build.gradle +++ b/sponge/build.gradle @@ -10,6 +10,7 @@ dependencies { implementation project(':common') implementation project(':sponge:sponge-service') implementation project(':sponge:sponge-service-api8') + compileOnly project(':common:loader-utils') compileOnly('org.spongepowered:spongeapi:8.0.0') { exclude(module: 'configurate-core') @@ -26,10 +27,9 @@ processResources { } shadowJar { - archiveFileName = "LuckPerms-Sponge-${project.ext.fullVersion}.jar" + archiveFileName = 'luckperms-sponge.jarinjar' dependencies { - include(dependency('net.luckperms:.*')) include(dependency('me.lucko.luckperms:.*')) } diff --git a/sponge/loader/build.gradle b/sponge/loader/build.gradle new file mode 100644 index 000000000..b7a89d919 --- /dev/null +++ b/sponge/loader/build.gradle @@ -0,0 +1,32 @@ +plugins { + id 'com.github.johnrengelman.shadow' +} + +repositories { + maven { url 'https://repo.spongepowered.org/repository/maven-public/' } +} + +dependencies { + compileOnly 'org.spongepowered:spongeapi:8.0.0' + + implementation project(':api') + implementation project(':common:loader-utils') +} + +processResources { + filesMatching('META-INF/sponge_plugins.json') { + expand 'pluginVersion': project.ext.fullVersion + } +} + +shadowJar { + archiveFileName = "LuckPerms-Sponge-${project.ext.fullVersion}.jar" + + from { + project(':sponge').tasks.shadowJar.archiveFile + } +} + +artifacts { + archives shadowJar +} \ No newline at end of file diff --git a/sponge/loader/src/main/java/me/lucko/luckperms/sponge/loader/SpongeLoaderPlugin.java b/sponge/loader/src/main/java/me/lucko/luckperms/sponge/loader/SpongeLoaderPlugin.java new file mode 100644 index 000000000..90710d013 --- /dev/null +++ b/sponge/loader/src/main/java/me/lucko/luckperms/sponge/loader/SpongeLoaderPlugin.java @@ -0,0 +1,75 @@ +/* + * 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.sponge.loader; + +import com.google.inject.Inject; +import com.google.inject.Injector; + +import me.lucko.luckperms.common.loader.JarInJarClassLoader; +import me.lucko.luckperms.common.loader.LoaderBootstrap; + +import org.spongepowered.api.Server; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.lifecycle.ConstructPluginEvent; +import org.spongepowered.api.event.lifecycle.StoppingEngineEvent; +import org.spongepowered.plugin.builtin.jvm.Plugin; + +import java.util.function.Supplier; + +@Plugin("luckperms") +public class SpongeLoaderPlugin implements Supplier { + private static final String JAR_NAME = "luckperms-sponge.jarinjar"; + private static final String BOOTSTRAP_CLASS = "me.lucko.luckperms.sponge.LPSpongeBootstrap"; + + private final LoaderBootstrap plugin; + private final Injector injector; + + @Inject + public SpongeLoaderPlugin(Injector injector) { + this.injector = injector; + + JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME); + this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, Supplier.class, this); + } + + @Override + public Injector get() { + return this.injector; + } + + @Listener(order = Order.FIRST) + public void onEnable(ConstructPluginEvent event) { + this.plugin.onLoad(); + this.plugin.onEnable(); + } + + @Listener + public void onDisable(StoppingEngineEvent event) { + this.plugin.onDisable(); + } + +} diff --git a/sponge/src/main/resources/META-INF/sponge_plugins.json b/sponge/loader/src/main/resources/META-INF/sponge_plugins.json similarity index 88% rename from sponge/src/main/resources/META-INF/sponge_plugins.json rename to sponge/loader/src/main/resources/META-INF/sponge_plugins.json index 324073eb2..50dc3de4d 100644 --- a/sponge/src/main/resources/META-INF/sponge_plugins.json +++ b/sponge/loader/src/main/resources/META-INF/sponge_plugins.json @@ -9,7 +9,7 @@ "id": "luckperms", "name": "LuckPerms", "version": "${pluginVersion}", - "entrypoint": "me.lucko.luckperms.sponge.LPSpongeBootstrap", + "entrypoint": "me.lucko.luckperms.sponge.loader.SpongeLoaderPlugin", "description": "A permissions plugin", "links": { "homepage": "https://luckperms.net" diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java index d74cd065d..3b91e497d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java @@ -26,9 +26,13 @@ package me.lucko.luckperms.sponge; import com.google.inject.Inject; +import com.google.inject.Injector; +import me.lucko.luckperms.common.loader.LoaderBootstrap; +import me.lucko.luckperms.common.plugin.bootstrap.BootstrappedWithLoader; import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; import me.lucko.luckperms.common.plugin.classpath.ClassPathAppender; +import me.lucko.luckperms.common.plugin.classpath.JarInJarClassPathAppender; import me.lucko.luckperms.common.plugin.logging.Log4jPluginLogger; import me.lucko.luckperms.common.plugin.logging.PluginLogger; import me.lucko.luckperms.common.util.MoreFiles; @@ -42,13 +46,8 @@ import org.spongepowered.api.config.ConfigDir; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.entity.living.player.server.ServerPlayer; -import org.spongepowered.api.event.Listener; -import org.spongepowered.api.event.Order; -import org.spongepowered.api.event.lifecycle.ConstructPluginEvent; -import org.spongepowered.api.event.lifecycle.StoppingEngineEvent; import org.spongepowered.api.profile.GameProfile; import org.spongepowered.plugin.PluginContainer; -import org.spongepowered.plugin.builtin.jvm.Plugin; import org.spongepowered.plugin.metadata.PluginMetadata; import java.io.IOException; @@ -62,12 +61,13 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.CountDownLatch; +import java.util.function.Supplier; /** * Bootstrap plugin for LuckPerms running on Sponge. */ -@Plugin("luckperms") -public class LPSpongeBootstrap implements LuckPermsBootstrap { +public class LPSpongeBootstrap implements LuckPermsBootstrap, LoaderBootstrap, BootstrappedWithLoader { + private final Object loader; /** * The plugin logger @@ -111,22 +111,31 @@ public class LPSpongeBootstrap implements LuckPermsBootstrap { /** * Injected configuration directory for the plugin */ - private final Path configDirectory; - @Inject - public LPSpongeBootstrap(Logger logger, Game game, PluginContainer pluginContainer, @ConfigDir(sharedRoot = false) Path configDirectory) { - this.logger = new Log4jPluginLogger(logger); - this.game = game; - this.pluginContainer = pluginContainer; - this.configDirectory = configDirectory; + @ConfigDir(sharedRoot = false) + private Path configDirectory; + + public LPSpongeBootstrap(Supplier loader) { + this.loader = loader; + + Injector injector = loader.get(); + this.logger = new Log4jPluginLogger(injector.getInstance(Logger.class)); + this.game = injector.getInstance(Game.class); + this.pluginContainer = injector.getInstance(PluginContainer.class); + injector.injectMembers(this); this.schedulerAdapter = new SpongeSchedulerAdapter(this.game, this.pluginContainer); - this.classPathAppender = new SpongeClassPathAppender(this); + this.classPathAppender = new JarInJarClassPathAppender(getClass().getClassLoader()); this.plugin = new LPSpongePlugin(this); } // provide adapters + @Override + public Object getLoader() { + return this.loader; + } + @Override public PluginLogger getPluginLogger() { return this.logger; @@ -143,15 +152,18 @@ public ClassPathAppender getClassPathAppender() { } // lifecycle - @Listener(order = Order.FIRST) - public void onEnable(ConstructPluginEvent event) { - this.startTime = Instant.now(); + + @Override + public void onLoad() { try { this.plugin.load(); } finally { this.loadLatch.countDown(); } + } + public void onEnable() { + this.startTime = Instant.now(); try { this.plugin.enable(); } finally { @@ -159,8 +171,7 @@ public void onEnable(ConstructPluginEvent event) { } } - @Listener - public void onDisable(StoppingEngineEvent event) { + public void onDisable() { this.plugin.disable(); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeClassPathAppender.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeClassPathAppender.java deleted file mode 100644 index efd521c50..000000000 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeClassPathAppender.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.sponge; - -import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; -import me.lucko.luckperms.common.plugin.classpath.ReflectionClassPathAppender; - -import java.lang.reflect.Field; -import java.net.URLClassLoader; - -public class SpongeClassPathAppender extends ReflectionClassPathAppender { - - private static URLClassLoader extractClassLoaderFromBootstrap(LuckPermsBootstrap bootstrap) { - ClassLoader classLoader = bootstrap.getClass().getClassLoader(); - - // try to cast directly to URLClassLoader in case things change in the future - if (classLoader instanceof URLClassLoader) { - return (URLClassLoader) classLoader; - } - - Class classLoaderClass = classLoader.getClass(); - - if (!classLoaderClass.getName().equals("cpw.mods.modlauncher.TransformingClassLoader")) { - throw new IllegalStateException("ClassLoader is not instance of TransformingClassLoader: " + classLoaderClass.getName()); - } - - try { - Field delegatedClassLoaderField = classLoaderClass.getDeclaredField("delegatedClassLoader"); - delegatedClassLoaderField.setAccessible(true); - Object delegatedClassLoader = delegatedClassLoaderField.get(classLoader); - return (URLClassLoader) delegatedClassLoader; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - public SpongeClassPathAppender(LuckPermsBootstrap bootstrap) throws IllegalStateException { - super(extractClassLoaderFromBootstrap(bootstrap)); - } - -} From 4bdcca6566bf522a09e5d123e89ed745a518ebc9 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 20 Feb 2022 22:41:58 +0000 Subject: [PATCH 075/314] Update mongodb driver --- common/build.gradle | 2 +- .../common/dependencies/Dependency.java | 32 ++++++++++++++++--- .../dependencies/DependencyRegistry.java | 2 +- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/common/build.gradle b/common/build.gradle index e4c43c1e2..b2137a5f0 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -72,6 +72,6 @@ dependencies { api 'com.zaxxer:HikariCP:4.0.3' api 'redis.clients:jedis:3.5.2' api 'com.rabbitmq:amqp-client:5.12.0' - api 'org.mongodb:mongo-java-driver:3.12.8' + api 'org.mongodb:mongodb-driver-legacy:4.5.0' api 'org.yaml:snakeyaml:1.28' } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index 8af936006..d45b30c89 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -197,11 +197,35 @@ public enum Dependency { "1.7.30", "zboHlk0btAoHYUhcax6ML4/Z6x0ZxTkorA1/lRAQXFc=" ), - MONGODB_DRIVER( + MONGODB_DRIVER_CORE( "org.mongodb", - "mongo-java-driver", - "3.12.8", - "92uqr4qaL3dbw5wrb8sQWQqFxpzr/Y/DhForeyg3taI=", + "mongodb-driver-core", + "4.5.0", + "awqoW0ImUcrCTA2d1rDCjDLEjLMCrOjKWIcC7E+zLGA=", + Relocation.of("mongodb", "com{}mongodb"), + Relocation.of("bson", "org{}bson") + ), + MONGODB_DRIVER_LEGACY( + "org.mongodb", + "mongodb-driver-legacy", + "4.5.0", + "77KZGIr3KZmzBpN69rGOLXmnlJIBCXRl/U4gEIdlFhY=", + Relocation.of("mongodb", "com{}mongodb"), + Relocation.of("bson", "org{}bson") + ), + MONGODB_DRIVER_SYNC( + "org.mongodb", + "mongodb-driver-sync", + "4.5.0", + "q9XDSGJjlo/Ek6jHoCbqWnaK/dghB8y9aDM0hCLiSvk=", + Relocation.of("mongodb", "com{}mongodb"), + Relocation.of("bson", "org{}bson") + ), + MONGODB_DRIVER_BSON( + "org.mongodb", + "bson", + "4.5.0", + "6CFyEzxbdeiBEXdDBmcgqWs5dvicgFkBLU3MlQUIqRA=", Relocation.of("mongodb", "com{}mongodb"), Relocation.of("bson", "org{}bson") ), diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java index 0be355473..3dfc287bd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java @@ -55,7 +55,7 @@ public class DependencyRegistry { .putAll(StorageType.JSON_COMBINED, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_GSON) .putAll(StorageType.HOCON_COMBINED, Dependency.HOCON_CONFIG, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_HOCON) .putAll(StorageType.TOML_COMBINED, Dependency.TOML4J, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_TOML) - .putAll(StorageType.MONGODB, Dependency.MONGODB_DRIVER) + .putAll(StorageType.MONGODB, Dependency.MONGODB_DRIVER_CORE, Dependency.MONGODB_DRIVER_LEGACY, Dependency.MONGODB_DRIVER_SYNC, Dependency.MONGODB_DRIVER_BSON) .putAll(StorageType.MARIADB, Dependency.MARIADB_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI) .putAll(StorageType.MYSQL, Dependency.MYSQL_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI) .putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI) From 8ed63159cee497ca0012f43c6f4d83ec65902cb7 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 27 Feb 2022 13:17:12 +0000 Subject: [PATCH 076/314] Set mongodb uuid representation (#3324) --- .../storage/implementation/mongodb/MongoStorage.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java index ab384c405..3320dc534 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java @@ -68,6 +68,7 @@ import net.luckperms.api.node.NodeBuilder; import org.bson.Document; +import org.bson.UuidRepresentation; import java.time.Instant; import java.util.ArrayList; @@ -111,8 +112,11 @@ public String getImplementationName() { @Override public void init() { + MongoClientOptions.Builder options = MongoClientOptions.builder() + .uuidRepresentation(UuidRepresentation.JAVA_LEGACY); + if (!Strings.isNullOrEmpty(this.connectionUri)) { - this.mongoClient = new MongoClient(new MongoClientURI(this.connectionUri)); + this.mongoClient = new MongoClient(new MongoClientURI(this.connectionUri, options)); } else { MongoCredential credential = null; if (!Strings.isNullOrEmpty(this.configuration.getUsername())) { @@ -129,9 +133,9 @@ public void init() { ServerAddress address = new ServerAddress(host, port); if (credential == null) { - this.mongoClient = new MongoClient(address); + this.mongoClient = new MongoClient(address, options.build()); } else { - this.mongoClient = new MongoClient(address, credential, MongoClientOptions.builder().build()); + this.mongoClient = new MongoClient(address, credential, options.build()); } } From 775f1adf367ef4ff6a756408f9cce5fb2fb2b7ca Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 28 Feb 2022 20:20:30 +0000 Subject: [PATCH 077/314] Support for command completions on Bukkit 1.18.2 --- bukkit/build.gradle | 2 +- .../me/lucko/luckperms/common/dependencies/Dependency.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 5e8dacd54..7dcf52230 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -17,7 +17,7 @@ dependencies { exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') } - compileOnly 'me.lucko:commodore:1.12' + compileOnly 'me.lucko:commodore:1.13' compileOnly('net.milkbowl.vault:VaultAPI:1.7') { exclude(module: 'bukkit') } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index d45b30c89..ee4fe72cd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -128,8 +128,8 @@ public enum Dependency { COMMODORE( "me{}lucko", "commodore", - "1.12", - "rLpLs20OG9TETCx1QzA+I+LY8P+p6Q9fUjLSmJcl2Kk=", + "1.13", + "A4M1Im54vrYbcdSpteQvI6ppNPYWL+VpfBVjsdteY+M=", Relocation.of("commodore", "me{}lucko{}commodore") ), COMMODORE_FILE( From f840c76e44c6f921d034de7449837888ff6b28e7 Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 28 Feb 2022 20:33:00 +0000 Subject: [PATCH 078/314] Don't pretty-print exporter json (#3329) --- .../main/java/me/lucko/luckperms/common/backup/Exporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java b/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java index 08b029bd2..1bb05f471 100644 --- a/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java +++ b/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java @@ -235,7 +235,7 @@ protected void processOutput(JsonObject json) { this.log.log("Finished gathering data, writing file..."); try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(this.filePath)), StandardCharsets.UTF_8))) { - GsonProvider.prettyPrinting().toJson(json, out); + GsonProvider.normal().toJson(json, out); } catch (IOException e) { e.printStackTrace(); } From 138f2c1b3e2ec7711614f79b0b920c8c58e673a1 Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 28 Feb 2022 20:45:15 +0000 Subject: [PATCH 079/314] Provide snakeyaml for Fabric (#3325) --- .../dependencies/DependencyRegistry.java | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java index 3dfc287bd..f705740c9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java @@ -25,8 +25,9 @@ package me.lucko.luckperms.common.dependencies; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.SetMultimap; import com.google.gson.JsonElement; import me.lucko.luckperms.common.config.ConfigKeys; @@ -46,23 +47,27 @@ */ public class DependencyRegistry { - private static final ListMultimap STORAGE_DEPENDENCIES = ImmutableListMultimap.builder() - .putAll(StorageType.YAML, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_YAML) - .putAll(StorageType.JSON, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_GSON) - .putAll(StorageType.HOCON, Dependency.HOCON_CONFIG, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_HOCON) - .putAll(StorageType.TOML, Dependency.TOML4J, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_TOML) - .putAll(StorageType.YAML_COMBINED, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_YAML) - .putAll(StorageType.JSON_COMBINED, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_GSON) - .putAll(StorageType.HOCON_COMBINED, Dependency.HOCON_CONFIG, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_HOCON) - .putAll(StorageType.TOML_COMBINED, Dependency.TOML4J, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_TOML) - .putAll(StorageType.MONGODB, Dependency.MONGODB_DRIVER_CORE, Dependency.MONGODB_DRIVER_LEGACY, Dependency.MONGODB_DRIVER_SYNC, Dependency.MONGODB_DRIVER_BSON) - .putAll(StorageType.MARIADB, Dependency.MARIADB_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI) - .putAll(StorageType.MYSQL, Dependency.MYSQL_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI) - .putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI) - .putAll(StorageType.SQLITE, Dependency.SQLITE_DRIVER) - .putAll(StorageType.H2, Dependency.H2_DRIVER) + private static final SetMultimap STORAGE_DEPENDENCIES = ImmutableSetMultimap.builder() + .putAll(StorageType.YAML, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_YAML, Dependency.SNAKEYAML) + .putAll(StorageType.YAML_COMBINED, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_YAML, Dependency.SNAKEYAML) + .putAll(StorageType.JSON, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_GSON) + .putAll(StorageType.JSON_COMBINED, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_GSON) + .putAll(StorageType.HOCON, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_HOCON, Dependency.HOCON_CONFIG) + .putAll(StorageType.HOCON_COMBINED, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_HOCON, Dependency.HOCON_CONFIG) + .putAll(StorageType.TOML, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_TOML, Dependency.TOML4J) + .putAll(StorageType.TOML_COMBINED, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_TOML, Dependency.TOML4J) + .putAll(StorageType.MONGODB, Dependency.MONGODB_DRIVER_CORE, Dependency.MONGODB_DRIVER_LEGACY, Dependency.MONGODB_DRIVER_SYNC, Dependency.MONGODB_DRIVER_BSON) + .putAll(StorageType.MARIADB, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI, Dependency.MARIADB_DRIVER) + .putAll(StorageType.MYSQL, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI, Dependency.MYSQL_DRIVER) + .putAll(StorageType.POSTGRESQL, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI, Dependency.POSTGRESQL_DRIVER) + .putAll(StorageType.SQLITE, Dependency.SQLITE_DRIVER) + .putAll(StorageType.H2, Dependency.H2_DRIVER) .build(); + private static final Set SNAKEYAML_PROVIDED_BY_PLATFORM = ImmutableSet.of( + Platform.Type.BUKKIT, Platform.Type.BUNGEECORD, Platform.Type.SPONGE, Platform.Type.NUKKIT + ); + private final LuckPermsPlugin plugin; public DependencyRegistry(LuckPermsPlugin plugin) { @@ -92,6 +97,11 @@ public Set resolveStorageDependencies(Set storageTypes) dependencies.remove(Dependency.SLF4J_SIMPLE); } + // don't load snakeyaml if it's provided by the platform + if (dependencies.contains(Dependency.SNAKEYAML) && SNAKEYAML_PROVIDED_BY_PLATFORM.contains(this.plugin.getBootstrap().getType())) { + dependencies.remove(Dependency.SNAKEYAML); + } + return dependencies; } @@ -104,8 +114,8 @@ public void applyRelocationSettings(Dependency dependency, List relo relocations.add(Relocation.of("gson", "com{}google{}gson")); } - // relocate yaml within configurate when running velocity - if (dependency == Dependency.CONFIGURATE_YAML && type == Platform.Type.VELOCITY) { + // relocate yaml within configurate if its being provided by LP + if (dependency == Dependency.CONFIGURATE_YAML && !SNAKEYAML_PROVIDED_BY_PLATFORM.contains(type)) { relocations.add(Relocation.of("yaml", "org{}yaml{}snakeyaml")); } } From eeec818286bdf81fa2c6422463eda960c801fb9e Mon Sep 17 00:00:00 2001 From: Marton Schneider Date: Wed, 16 Mar 2022 22:01:21 +0100 Subject: [PATCH 080/314] Allow server name to be specified with an env var (#3341) --- .../main/java/me/lucko/luckperms/common/config/ConfigKeys.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 38a228a41..ed27e90a7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -93,6 +93,9 @@ private ConfigKeys() {} if (server.equals("load-from-system-property")) { server = System.getProperty("luckperms.server", "global").toLowerCase(Locale.ROOT); } + if (server.equals("load-from-environment-variable")) { + System.getenv().getOrDefault("LUCKPERMS_SERVER", "global").toLowerCase(Locale.ROOT); + } return server; }); From 405c5c3cf9cd16d44fef280955e89eaf59ab7d11 Mon Sep 17 00:00:00 2001 From: Marton Schneider Date: Fri, 18 Mar 2022 22:01:01 +0100 Subject: [PATCH 081/314] Fix typo with env var reading (#3345) * resolves LuckPerms/LuckPerms#3339 * change server name from env var => add to a separate option for this * fix missing variable assignment --- .../main/java/me/lucko/luckperms/common/config/ConfigKeys.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index ed27e90a7..03b0b837c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -94,7 +94,7 @@ private ConfigKeys() {} server = System.getProperty("luckperms.server", "global").toLowerCase(Locale.ROOT); } if (server.equals("load-from-environment-variable")) { - System.getenv().getOrDefault("LUCKPERMS_SERVER", "global").toLowerCase(Locale.ROOT); + server = System.getenv().getOrDefault("LUCKPERMS_SERVER", "global").toLowerCase(Locale.ROOT); } return server; }); From fb56189f0a367961e81ac43e87c3dac830da43cf Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 19 Mar 2022 23:30:31 +0000 Subject: [PATCH 082/314] Improve AbstractJavaScheduler (#3326) --- .../bukkit/BukkitSchedulerAdapter.java | 1 + .../scheduler/AbstractJavaScheduler.java | 98 ++++++++++--------- .../fabric/FabricSchedulerAdapter.java | 1 + .../nukkit/NukkitSchedulerAdapter.java | 1 + 4 files changed, 53 insertions(+), 48 deletions(-) diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSchedulerAdapter.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSchedulerAdapter.java index 93095e3bf..90fbe7f12 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSchedulerAdapter.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSchedulerAdapter.java @@ -34,6 +34,7 @@ public class BukkitSchedulerAdapter extends AbstractJavaScheduler implements Sch private final Executor sync; public BukkitSchedulerAdapter(LPBukkitBootstrap bootstrap) { + super(bootstrap); this.sync = r -> bootstrap.getServer().getScheduler().scheduleSyncDelayedTask(bootstrap.getLoader(), r); } diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/scheduler/AbstractJavaScheduler.java b/common/src/main/java/me/lucko/luckperms/common/plugin/scheduler/AbstractJavaScheduler.java index 1c170c836..e1583e877 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/scheduler/AbstractJavaScheduler.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/scheduler/AbstractJavaScheduler.java @@ -25,40 +25,44 @@ package me.lucko.luckperms.common.plugin.scheduler; -import com.google.common.util.concurrent.ThreadFactoryBuilder; - -import org.checkerframework.checker.nullness.qual.NonNull; +import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Arrays; import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Abstract implementation of {@link SchedulerAdapter} using a {@link ScheduledExecutorService}. */ public abstract class AbstractJavaScheduler implements SchedulerAdapter { + private static final int PARALLELISM = 16; + + private final LuckPermsBootstrap bootstrap; + private final ScheduledThreadPoolExecutor scheduler; - private final ErrorReportingExecutor schedulerWorkerPool; private final ForkJoinPool worker; - public AbstractJavaScheduler() { - this.scheduler = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder() - .setDaemon(true) - .setNameFormat("luckperms-scheduler") - .build() - ); + public AbstractJavaScheduler(LuckPermsBootstrap bootstrap) { + this.bootstrap = bootstrap; + + this.scheduler = new ScheduledThreadPoolExecutor(1, r -> { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setName("luckperms-scheduler"); + return thread; + }); this.scheduler.setRemoveOnCancelPolicy(true); - this.schedulerWorkerPool = new ErrorReportingExecutor(Executors.newCachedThreadPool(new ThreadFactoryBuilder() - .setDaemon(true) - .setNameFormat("luckperms-scheduler-worker-%d") - .build() - )); - this.worker = new ForkJoinPool(32, ForkJoinPool.defaultForkJoinWorkerThreadFactory, (t, e) -> e.printStackTrace(), false); + this.scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.worker = new ForkJoinPool(PARALLELISM, new WorkerThreadFactory(), new ExceptionHandler(), false); } @Override @@ -68,13 +72,13 @@ public Executor async() { @Override public SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit) { - ScheduledFuture future = this.scheduler.schedule(() -> this.schedulerWorkerPool.execute(task), delay, unit); + ScheduledFuture future = this.scheduler.schedule(() -> this.worker.execute(task), delay, unit); return () -> future.cancel(false); } @Override public SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit) { - ScheduledFuture future = this.scheduler.scheduleAtFixedRate(() -> this.schedulerWorkerPool.execute(task), interval, interval, unit); + ScheduledFuture future = this.scheduler.scheduleAtFixedRate(() -> this.worker.execute(task), interval, interval, unit); return () -> future.cancel(false); } @@ -82,7 +86,10 @@ public SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit) public void shutdownScheduler() { this.scheduler.shutdown(); try { - this.scheduler.awaitTermination(1, TimeUnit.MINUTES); + if (!this.scheduler.awaitTermination(1, TimeUnit.MINUTES)) { + this.bootstrap.getPluginLogger().severe("Timed out waiting for the LuckPerms scheduler to terminate"); + reportRunningTasks(thread -> thread.getName().equals("luckperms-scheduler")); + } } catch (InterruptedException e) { e.printStackTrace(); } @@ -90,48 +97,43 @@ public void shutdownScheduler() { @Override public void shutdownExecutor() { - this.schedulerWorkerPool.delegate.shutdown(); - try { - this.schedulerWorkerPool.delegate.awaitTermination(1, TimeUnit.MINUTES); - } catch (InterruptedException e) { - e.printStackTrace(); - } - this.worker.shutdown(); try { - this.worker.awaitTermination(1, TimeUnit.MINUTES); + if (!this.worker.awaitTermination(1, TimeUnit.MINUTES)) { + this.bootstrap.getPluginLogger().severe("Timed out waiting for the LuckPerms worker thread pool to terminate"); + reportRunningTasks(thread -> thread.getName().startsWith("luckperms-worker-")); + } } catch (InterruptedException e) { e.printStackTrace(); } } - private static final class ErrorReportingExecutor implements Executor { - private final ExecutorService delegate; + private void reportRunningTasks(Predicate predicate) { + Thread.getAllStackTraces().forEach((thread, stack) -> { + if (predicate.test(thread)) { + this.bootstrap.getPluginLogger().warn("Thread " + thread.getName() + " is blocked, and may be the reason for the slow shutdown!\n" + + Arrays.stream(stack).map(el -> " " + el).collect(Collectors.joining("\n")) + ); + } + }); + } - private ErrorReportingExecutor(ExecutorService delegate) { - this.delegate = delegate; - } + private static final class WorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + private static final AtomicInteger COUNT = new AtomicInteger(0); @Override - public void execute(@NonNull Runnable command) { - this.delegate.execute(new ErrorReportingRunnable(command)); + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setDaemon(true); + thread.setName("luckperms-worker-" + COUNT.getAndIncrement()); + return thread; } } - private static final class ErrorReportingRunnable implements Runnable { - private final Runnable delegate; - - private ErrorReportingRunnable(Runnable delegate) { - this.delegate = delegate; - } - + private final class ExceptionHandler implements UncaughtExceptionHandler { @Override - public void run() { - try { - this.delegate.run(); - } catch (Exception e) { - e.printStackTrace(); - } + public void uncaughtException(Thread t, Throwable e) { + AbstractJavaScheduler.this.bootstrap.getPluginLogger().warn("Thread " + t.getName() + " threw an uncaught exception", e); } } } diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSchedulerAdapter.java b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSchedulerAdapter.java index 30fc6315d..d7abc4798 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSchedulerAdapter.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricSchedulerAdapter.java @@ -33,6 +33,7 @@ public class FabricSchedulerAdapter extends AbstractJavaScheduler { private final Executor sync; public FabricSchedulerAdapter(LPFabricBootstrap bootstrap) { + super(bootstrap); this.sync = r -> bootstrap.getServer().orElseThrow(() -> new IllegalStateException("Server not ready")).submitAndJoin(r); } diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/NukkitSchedulerAdapter.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/NukkitSchedulerAdapter.java index 8e33cb687..003ee229d 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/NukkitSchedulerAdapter.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/NukkitSchedulerAdapter.java @@ -34,6 +34,7 @@ public class NukkitSchedulerAdapter extends AbstractJavaScheduler implements Sch private final Executor sync; public NukkitSchedulerAdapter(LPNukkitBootstrap bootstrap) { + super(bootstrap); this.sync = r -> bootstrap.getServer().getScheduler().scheduleTask(bootstrap.getLoader(), r, false); } From eece15d4159e9821e6ec144c2eaed27b00b93723 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 20 Mar 2022 10:45:59 +0000 Subject: [PATCH 083/314] Close web editor sockets on plugin disable (#3326) --- .../common/plugin/AbstractLuckPermsPlugin.java | 8 ++++++++ .../common/webeditor/socket/WebEditorSocket.java | 1 + .../common/webeditor/store/WebEditorSocketMap.java | 11 +++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index f6f2fd806..9bb3b7c1a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -58,6 +58,7 @@ import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; +import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; import me.lucko.luckperms.common.webeditor.store.WebEditorStore; import net.luckperms.api.LuckPerms; @@ -234,6 +235,13 @@ public final void disable() { // cancel delayed/repeating tasks getBootstrap().getScheduler().shutdownScheduler(); + // close web editor sockets + for (WebEditorSocket socket : this.webEditorStore.sockets().getSockets()) { + if (!socket.isClosed()) { + socket.close(); + } + } + // shutdown permission vault and verbose handler tasks this.permissionRegistry.close(); this.verboseHandler.close(); diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java index 74c82f17a..221b502e0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/socket/WebEditorSocket.java @@ -217,6 +217,7 @@ public void close() { private void closeSocket() { this.socket.socket().close(1000, "Normal"); + this.plugin.getWebEditorStore().sockets().removeSocket(this); this.closed = true; } diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSocketMap.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSocketMap.java index 9e7fb2de7..9462cc6dc 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSocketMap.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/store/WebEditorSocketMap.java @@ -31,13 +31,12 @@ import me.lucko.luckperms.common.util.CaffeineFactory; import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; +import java.util.Collection; import java.util.UUID; -import java.util.concurrent.TimeUnit; public final class WebEditorSocketMap { private final Cache sockets = CaffeineFactory.newBuilder() .weakValues() - .expireAfterWrite(5, TimeUnit.MINUTES) .build(); public WebEditorSocket getSocket(Sender sender) { @@ -47,4 +46,12 @@ public WebEditorSocket getSocket(Sender sender) { public void putSocket(Sender sender, WebEditorSocket socket) { this.sockets.put(sender.getUniqueId(), socket); } + + public void removeSocket(WebEditorSocket socket) { + this.sockets.asMap().values().remove(socket); + } + + public Collection getSockets() { + return this.sockets.asMap().values(); + } } From 5647b2e5c6b13f685f492e49e4af83d7ffa402fa Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 23 Mar 2022 20:15:03 +0000 Subject: [PATCH 084/314] Shutdown okhttp on plugin disable (#3347) --- .../common/plugin/AbstractLuckPermsPlugin.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 9bb3b7c1a..e986a0dd5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -91,6 +91,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { private PermissionRegistry permissionRegistry; private LogDispatcher logDispatcher; private LuckPermsConfiguration configuration; + private OkHttpClient httpClient; private BytebinClient bytebin; private BytesocksClient bytesocks; private WebEditorStore webEditorStore; @@ -134,12 +135,12 @@ public final void enable() { this.configuration = new LuckPermsConfiguration(this, provideConfigurationAdapter()); // setup a bytebin instance - OkHttpClient httpClient = new OkHttpClient.Builder() + this.httpClient = new OkHttpClient.Builder() .callTimeout(15, TimeUnit.SECONDS) .build(); - this.bytebin = new BytebinClient(httpClient, getConfiguration().get(ConfigKeys.BYTEBIN_URL), "luckperms"); - this.bytesocks = new BytesocksClient(httpClient, getConfiguration().get(ConfigKeys.BYTESOCKS_HOST), "luckperms/editor"); + this.bytebin = new BytebinClient(this.httpClient, getConfiguration().get(ConfigKeys.BYTEBIN_URL), "luckperms"); + this.bytesocks = new BytesocksClient(this.httpClient, getConfiguration().get(ConfigKeys.BYTESOCKS_HOST), "luckperms/editor"); this.webEditorStore = new WebEditorStore(this); // init translation repo and update bundle files @@ -273,6 +274,10 @@ public final void disable() { // shutdown async executor pool getBootstrap().getScheduler().shutdownExecutor(); + // shutdown okhttp + this.httpClient.dispatcher().executorService().shutdown(); + this.httpClient.connectionPool().evictAll(); + // close classpath appender getBootstrap().getClassPathAppender().close(); From 2e0a4d71a5d442e30762a64dd854d2c05226cfdc Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 27 Mar 2022 16:50:31 +0100 Subject: [PATCH 085/314] Fix redis close (#3353) --- .../messaging/redis/RedisMessenger.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java b/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java index 73cf911d9..c673730bf 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/redis/RedisMessenger.java @@ -48,8 +48,9 @@ public class RedisMessenger implements Messenger { private final LuckPermsPlugin plugin; private final IncomingMessageConsumer consumer; - private JedisPool jedisPool; - private Subscription sub; + private /* final */ JedisPool jedisPool; + private /* final */ Subscription sub; + private boolean closing = false; public RedisMessenger(LuckPermsPlugin plugin, IncomingMessageConsumer consumer) { this.plugin = plugin; @@ -82,6 +83,7 @@ public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { @Override public void close() { + this.closing = true; this.sub.unsubscribe(); this.jedisPool.destroy(); } @@ -90,16 +92,21 @@ private class Subscription extends JedisPubSub implements Runnable { @Override public void run() { - boolean wasBroken = false; - while (!Thread.interrupted() && !RedisMessenger.this.jedisPool.isClosed()) { + boolean first = true; + while (!RedisMessenger.this.closing && !Thread.interrupted() && !RedisMessenger.this.jedisPool.isClosed()) { try (Jedis jedis = RedisMessenger.this.jedisPool.getResource()) { - if (wasBroken) { + if (first) { + first = false; + } else { RedisMessenger.this.plugin.getLogger().info("Redis pubsub connection re-established"); - wasBroken = false; } - jedis.subscribe(this, CHANNEL); + + jedis.subscribe(this, CHANNEL); // blocking call } catch (Exception e) { - wasBroken = true; + if (RedisMessenger.this.closing) { + return; + } + RedisMessenger.this.plugin.getLogger().warn("Redis pubsub connection dropped, trying to re-open the connection", e); try { unsubscribe(); From 126631fce8b60b5548ae5d932722e8bb667d28b1 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 3 Apr 2022 15:21:18 +0100 Subject: [PATCH 086/314] Load configuration from env or system props (#3294) --- .../luckperms/bukkit/BukkitConfigAdapter.java | 13 -- .../vault/LuckPermsVaultPermission.java | 8 +- .../luckperms/bungee/BungeeConfigAdapter.java | 11 -- .../luckperms/common/config/ConfigKeys.java | 20 +-- .../adapter/ConfigurateConfigAdapter.java | 10 -- .../generic/adapter/ConfigurationAdapter.java | 2 - .../EnvironmentVariableConfigAdapter.java | 68 ++++++++++ .../adapter/MultiConfigurationAdapter.java | 117 ++++++++++++++++++ .../StringBasedConfigurationAdapter.java | 99 +++++++++++++++ .../adapter/SystemPropertyConfigAdapter.java | 64 ++++++++++ .../plugin/AbstractLuckPermsPlugin.java | 10 +- .../luckperms/nukkit/NukkitConfigAdapter.java | 13 -- 12 files changed, 366 insertions(+), 69 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/EnvironmentVariableConfigAdapter.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/MultiConfigurationAdapter.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/StringBasedConfigurationAdapter.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/SystemPropertyConfigAdapter.java diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitConfigAdapter.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitConfigAdapter.java index 1f3107253..8538c6560 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitConfigAdapter.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitConfigAdapter.java @@ -32,11 +32,9 @@ import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; public class BukkitConfigAdapter implements ConfigurationAdapter { private final LuckPermsPlugin plugin; @@ -75,17 +73,6 @@ public List getStringList(String path, List def) { return this.configuration.isSet(path) ? list : def; } - @Override - public List getKeys(String path, List def) { - ConfigurationSection section = this.configuration.getConfigurationSection(path); - if (section == null) { - return def; - } - - Set keys = section.getKeys(false); - return keys == null ? def : new ArrayList<>(keys); - } - @Override public Map getStringMap(String path, Map def) { Map map = new HashMap<>(); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java index 5776a182a..dacc723b6 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java @@ -34,6 +34,7 @@ import me.lucko.luckperms.common.cacheddata.type.PermissionCache; import me.lucko.luckperms.common.calculator.processor.DirectProcessor; import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.config.LuckPermsConfiguration; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.HolderType; import me.lucko.luckperms.common.model.PermissionHolder; @@ -470,7 +471,12 @@ String getServer() { } String getVaultServer() { - return this.plugin.getConfiguration().get(ConfigKeys.VAULT_SERVER); + LuckPermsConfiguration configuration = this.plugin.getConfiguration(); + if (configuration.get(ConfigKeys.USE_VAULT_SERVER)) { + return configuration.get(ConfigKeys.VAULT_SERVER); + } else { + return configuration.get(ConfigKeys.SERVER); + } } boolean isIncludeGlobal() { diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeConfigAdapter.java b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeConfigAdapter.java index edfae1728..c32261981 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeConfigAdapter.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeConfigAdapter.java @@ -34,7 +34,6 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -80,16 +79,6 @@ public List getStringList(String path, List def) { return Optional.ofNullable(this.configuration.getStringList(path)).orElse(def); } - @Override - public List getKeys(String path, List def) { - Configuration section = this.configuration.getSection(path); - if (section == null) { - return def; - } - - return Optional.of((List) new ArrayList<>(section.getKeys())).orElse(def); - } - @Override public Map getStringMap(String path, Map def) { Map map = new HashMap<>(); diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 03b0b837c..2c5ac5a31 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -88,16 +88,7 @@ private ConfigKeys() {} /** * The name of the server */ - public static final ConfigKey SERVER = key(c -> { - String server = c.getString("server", "global").toLowerCase(Locale.ROOT); - if (server.equals("load-from-system-property")) { - server = System.getProperty("luckperms.server", "global").toLowerCase(Locale.ROOT); - } - if (server.equals("load-from-environment-variable")) { - server = System.getenv().getOrDefault("LUCKPERMS_SERVER", "global").toLowerCase(Locale.ROOT); - } - return server; - }); + public static final ConfigKey SERVER = lowercaseStringKey("server", "global"); /** * How many minutes to wait between syncs. A value <= 0 will disable syncing. @@ -501,14 +492,7 @@ private ConfigKeys() {} /** * The name of the server to use for Vault. */ - public static final ConfigKey VAULT_SERVER = key(c -> { - // default to true for backwards compatibility - if (USE_VAULT_SERVER.get(c)) { - return c.getString("vault-server", "global").toLowerCase(Locale.ROOT); - } else { - return SERVER.get(c); - } - }); + public static final ConfigKey VAULT_SERVER = lowercaseStringKey("vault-server", "global"); /** * If Vault should apply global permissions diff --git a/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/ConfigurateConfigAdapter.java b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/ConfigurateConfigAdapter.java index 3a02cfce3..4362f37f3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/ConfigurateConfigAdapter.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/ConfigurateConfigAdapter.java @@ -95,16 +95,6 @@ public List getStringList(String path, List def) { return node.getList(Object::toString); } - @Override - public List getKeys(String path, List def) { - ConfigurationNode node = resolvePath(path); - if (node.isVirtual() || !node.isMap()) { - return def; - } - - return node.getChildrenMap().keySet().stream().map(Object::toString).collect(Collectors.toList()); - } - @SuppressWarnings("unchecked") @Override public Map getStringMap(String path, Map def) { diff --git a/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/ConfigurationAdapter.java b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/ConfigurationAdapter.java index d91d67eab..e732845c7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/ConfigurationAdapter.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/ConfigurationAdapter.java @@ -44,8 +44,6 @@ public interface ConfigurationAdapter { List getStringList(String path, List def); - List getKeys(String path, List def); - Map getStringMap(String path, Map def); } diff --git a/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/EnvironmentVariableConfigAdapter.java b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/EnvironmentVariableConfigAdapter.java new file mode 100644 index 000000000..abc11b437 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/EnvironmentVariableConfigAdapter.java @@ -0,0 +1,68 @@ +/* + * 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.config.generic.adapter; + +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Locale; + +public class EnvironmentVariableConfigAdapter extends StringBasedConfigurationAdapter { + private static final String PREFIX = "LUCKPERMS_"; + + private final LuckPermsPlugin plugin; + + public EnvironmentVariableConfigAdapter(LuckPermsPlugin plugin) { + this.plugin = plugin; + } + + @Override + protected @Nullable String resolveValue(String path) { + // e.g. + // 'server' -> LUCKPERMS_SERVER + // 'data.table_prefix' -> LUCKPERMS_DATA_TABLE_PREFIX + String key = PREFIX + path.toUpperCase(Locale.ROOT) + .replace('-', '_') + .replace('.', '_'); + + String value = System.getenv(key); + if (value != null) { + this.plugin.getLogger().info("Resolved configuration value from environment variable: " + key + " = " + (path.contains("password") ? "*****" : value)); + } + return value; + } + + @Override + public LuckPermsPlugin getPlugin() { + return this.plugin; + } + + @Override + public void reload() { + // no-op + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/MultiConfigurationAdapter.java b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/MultiConfigurationAdapter.java new file mode 100644 index 000000000..fcdf1b876 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/MultiConfigurationAdapter.java @@ -0,0 +1,117 @@ +/* + * 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.config.generic.adapter; + +import com.google.common.collect.ImmutableList; + +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import java.util.List; +import java.util.Map; + +/** + * A {@link ConfigurationAdapter} composed of one or more other ConfigurationAdapters. + */ +public class MultiConfigurationAdapter implements ConfigurationAdapter { + private final LuckPermsPlugin plugin; + private final List adapters; + + /** + * Creates a {@link MultiConfigurationAdapter}. + * + *

    The first adapter in the list has priority (the final say) in deciding what the value is. + * All adapters are tried in reverse order, and the value returned from the previous adapter + * is passed into the next as the {@code def} value.

    + * + * @param plugin the plugin + * @param adapters a list of adapters + */ + public MultiConfigurationAdapter(LuckPermsPlugin plugin, List adapters) { + this.plugin = plugin; + this.adapters = ImmutableList.copyOf(adapters).reverse(); + } + + public MultiConfigurationAdapter(LuckPermsPlugin plugin, ConfigurationAdapter... adapters) { + this(plugin, ImmutableList.copyOf(adapters)); + } + + @Override + public LuckPermsPlugin getPlugin() { + return this.plugin; + } + + @Override + public void reload() { + for (ConfigurationAdapter adapter : this.adapters) { + adapter.reload(); + } + } + + @Override + public String getString(String path, String def) { + String result = def; + for (ConfigurationAdapter adapter : this.adapters) { + result = adapter.getString(path, result); + } + return result; + } + + @Override + public int getInteger(String path, int def) { + int result = def; + for (ConfigurationAdapter adapter : this.adapters) { + result = adapter.getInteger(path, result); + } + return result; + } + + @Override + public boolean getBoolean(String path, boolean def) { + boolean result = def; + for (ConfigurationAdapter adapter : this.adapters) { + result = adapter.getBoolean(path, result); + } + return result; + } + + @Override + public List getStringList(String path, List def) { + List result = def; + for (ConfigurationAdapter adapter : this.adapters) { + result = adapter.getStringList(path, result); + } + return result; + } + + @Override + public Map getStringMap(String path, Map def) { + Map result = def; + for (ConfigurationAdapter adapter : this.adapters) { + result = adapter.getStringMap(path, result); + } + return result; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/StringBasedConfigurationAdapter.java b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/StringBasedConfigurationAdapter.java new file mode 100644 index 000000000..6b2f5fcf2 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/StringBasedConfigurationAdapter.java @@ -0,0 +1,99 @@ +/* + * 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.config.generic.adapter; + +import com.google.common.base.Splitter; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.Map; + +public abstract class StringBasedConfigurationAdapter implements ConfigurationAdapter { + + private static final Splitter LIST_SPLITTER = Splitter.on(','); + private static final Splitter.MapSplitter MAP_SPLITTER = Splitter.on(',').withKeyValueSeparator('='); + + protected abstract @Nullable String resolveValue(String path); + + @Override + public String getString(String path, String def) { + String value = resolveValue(path); + if (value == null) { + return def; + } + + return value; + } + + @Override + public int getInteger(String path, int def) { + String value = resolveValue(path); + if (value == null) { + return def; + } + + try { + return Integer.parseInt(value); + } catch (IllegalArgumentException e) { + return def; + } + } + + @Override + public boolean getBoolean(String path, boolean def) { + String value = resolveValue(path); + if (value == null) { + return def; + } + + try { + return Boolean.parseBoolean(value); + } catch (IllegalArgumentException e) { + return def; + } + } + + @Override + public List getStringList(String path, List def) { + String value = resolveValue(path); + if (value == null) { + return def; + } + + return LIST_SPLITTER.splitToList(value); + } + + @Override + public Map getStringMap(String path, Map def) { + String value = resolveValue(path); + if (value == null) { + return def; + } + + return MAP_SPLITTER.split(value); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/SystemPropertyConfigAdapter.java b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/SystemPropertyConfigAdapter.java new file mode 100644 index 000000000..5b0bee744 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/config/generic/adapter/SystemPropertyConfigAdapter.java @@ -0,0 +1,64 @@ +/* + * 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.config.generic.adapter; + +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import org.checkerframework.checker.nullness.qual.Nullable; + +public class SystemPropertyConfigAdapter extends StringBasedConfigurationAdapter { + private static final String PREFIX = "luckperms."; + + private final LuckPermsPlugin plugin; + + public SystemPropertyConfigAdapter(LuckPermsPlugin plugin) { + this.plugin = plugin; + } + + @Override + protected @Nullable String resolveValue(String path) { + // e.g. + // 'server' -> luckperms.server + // 'data.table_prefix' -> luckperms.data.table-prefix + String key = PREFIX + path; + + String value = System.getProperty(key); + if (value != null) { + this.plugin.getLogger().info("Resolved configuration value from system property: " + key + " = " + (path.contains("password") ? "*****" : value)); + } + return value; + } + + @Override + public LuckPermsPlugin getPlugin() { + return this.plugin; + } + + @Override + public void reload() { + // no-op + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index e986a0dd5..24ed279c7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -32,6 +32,9 @@ import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.LuckPermsConfiguration; import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter; +import me.lucko.luckperms.common.config.generic.adapter.EnvironmentVariableConfigAdapter; +import me.lucko.luckperms.common.config.generic.adapter.MultiConfigurationAdapter; +import me.lucko.luckperms.common.config.generic.adapter.SystemPropertyConfigAdapter; import me.lucko.luckperms.common.context.calculator.ConfigurationContextCalculator; import me.lucko.luckperms.common.dependencies.Dependency; import me.lucko.luckperms.common.dependencies.DependencyManager; @@ -132,7 +135,12 @@ public final void enable() { // load configuration getLogger().info("Loading configuration..."); - this.configuration = new LuckPermsConfiguration(this, provideConfigurationAdapter()); + ConfigurationAdapter configFileAdapter = provideConfigurationAdapter(); + this.configuration = new LuckPermsConfiguration(this, new MultiConfigurationAdapter(this, + new SystemPropertyConfigAdapter(this), + new EnvironmentVariableConfigAdapter(this), + configFileAdapter + )); // setup a bytebin instance this.httpClient = new OkHttpClient.Builder() diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/NukkitConfigAdapter.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/NukkitConfigAdapter.java index e53937d4a..e53c0b08f 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/NukkitConfigAdapter.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/NukkitConfigAdapter.java @@ -32,11 +32,9 @@ import cn.nukkit.utils.ConfigSection; import java.io.File; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; public class NukkitConfigAdapter implements ConfigurationAdapter { private final LuckPermsPlugin plugin; @@ -75,17 +73,6 @@ public List getStringList(String path, List def) { return list == null ? def : list; } - @Override - public List getKeys(String path, List def) { - ConfigSection section = this.configuration.getSection(path); - if (section == null) { - return def; - } - - Set keys = section.getKeys(false); - return keys == null ? def : new ArrayList<>(keys); - } - @Override public Map getStringMap(String path, Map def) { Map map = new HashMap<>(); From 34873914bd30ba00a861b61e8c68d8486afeabec Mon Sep 17 00:00:00 2001 From: Zeromaniac <94971971+ZeromaniacMc@users.noreply.github.com> Date: Fri, 8 Apr 2022 21:35:40 +0200 Subject: [PATCH 087/314] Show sql table prefix if set in '/lp info' (#3360) --- .../implementation/mongodb/MongoStorage.java | 7 +++++++ .../storage/implementation/sql/SqlStorage.java | 13 ++++++++++++- .../sql/connection/ConnectionFactory.java | 3 ++- .../connection/file/FlatfileConnectionFactory.java | 5 ++++- common/src/main/resources/luckperms_en.properties | 2 ++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java index 3320dc534..916dfae57 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java @@ -173,6 +173,13 @@ public Map getMeta() { Message.formatBoolean(success) ); + if (!this.prefix.isEmpty()) { + meta.put( + Component.translatable("luckperms.command.info.storage.meta.collection-prefix-key"), + Component.text(this.prefix) + ); + } + return meta; } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index 30f839ac4..ba2d4ce72 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -34,6 +34,7 @@ import me.lucko.luckperms.common.bulkupdate.BulkUpdate; import me.lucko.luckperms.common.bulkupdate.BulkUpdateStatistics; import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder; +import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.context.serializer.ContextSetJsonSerializer; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Track; @@ -230,7 +231,17 @@ public void shutdown() { @Override public Map getMeta() { - return this.connectionFactory.getMeta(); + Map meta = this.connectionFactory.getMeta(); + + String tablePrefix = this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX); + if (!tablePrefix.equals("luckperms_")) { + meta.put( + Component.translatable("luckperms.command.info.storage.meta.table-prefix-key"), + Component.text(tablePrefix) + ); + } + + return meta; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/ConnectionFactory.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/ConnectionFactory.java index 27e84bcd4..36014e1c9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/ConnectionFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/ConnectionFactory.java @@ -32,6 +32,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Function; @@ -44,7 +45,7 @@ public interface ConnectionFactory { void shutdown() throws Exception; default Map getMeta() { - return Collections.emptyMap(); + return new LinkedHashMap<>(); } Function getStatementProcessor(); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/file/FlatfileConnectionFactory.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/file/FlatfileConnectionFactory.java index a80c7edee..368c34785 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/file/FlatfileConnectionFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/connection/file/FlatfileConnectionFactory.java @@ -37,6 +37,7 @@ import java.sql.SQLException; import java.text.DecimalFormat; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -119,9 +120,11 @@ public Map getMeta() { fileSize = "0MB"; } - return Collections.singletonMap( + Map meta = new LinkedHashMap<>(); + meta.put( Component.translatable("luckperms.command.info.storage.meta.file-size-key"), Component.text(fileSize, NamedTextColor.GREEN) ); + return meta; } } diff --git a/common/src/main/resources/luckperms_en.properties b/common/src/main/resources/luckperms_en.properties index 6ce54db35..a0fb7cd47 100644 --- a/common/src/main/resources/luckperms_en.properties +++ b/common/src/main/resources/luckperms_en.properties @@ -151,6 +151,8 @@ luckperms.command.info.storage.meta.split-types-key=Types luckperms.command.info.storage.meta.ping-key=Ping luckperms.command.info.storage.meta.connected-key=Connected luckperms.command.info.storage.meta.file-size-key=File Size +luckperms.command.info.storage.meta.table-prefix-key=Table Prefix +luckperms.command.info.storage.meta.collection-prefix-key=Collection Prefix luckperms.command.info.extensions-key=Extensions luckperms.command.info.messaging-key=Messaging luckperms.command.info.instance-key=Instance From 207e4aa9f8c640ad3b17f844d79512f7b8fb2044 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 8 Apr 2022 21:02:31 +0100 Subject: [PATCH 088/314] Show table prefix in a different colour --- .../common/storage/implementation/mongodb/MongoStorage.java | 2 +- .../common/storage/implementation/sql/SqlStorage.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java index 916dfae57..ba71f33db 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java @@ -176,7 +176,7 @@ public Map getMeta() { if (!this.prefix.isEmpty()) { meta.put( Component.translatable("luckperms.command.info.storage.meta.collection-prefix-key"), - Component.text(this.prefix) + Component.text(this.prefix, NamedTextColor.WHITE) ); } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index ba2d4ce72..0c063e6c3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -52,6 +52,7 @@ import me.lucko.luckperms.common.util.gson.GsonProvider; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.luckperms.api.actionlog.Action; import net.luckperms.api.context.DefaultContextKeys; import net.luckperms.api.context.MutableContextSet; @@ -237,7 +238,7 @@ public Map getMeta() { if (!tablePrefix.equals("luckperms_")) { meta.put( Component.translatable("luckperms.command.info.storage.meta.table-prefix-key"), - Component.text(tablePrefix) + Component.text(tablePrefix, NamedTextColor.WHITE) ); } From b3cdf67c4fab0f56cdcf90f03a243ebd715657c6 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 24 Apr 2022 09:33:33 +0100 Subject: [PATCH 089/314] Add error logging for failed plugin message parse (#3364) --- .../messaging/LuckPermsMessagingService.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/LuckPermsMessagingService.java b/common/src/main/java/me/lucko/luckperms/common/messaging/LuckPermsMessagingService.java index a49f283ea..a18e8682b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/LuckPermsMessagingService.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/LuckPermsMessagingService.java @@ -164,11 +164,21 @@ public boolean consumeIncomingMessage(@NonNull Message message) { @Override public boolean consumeIncomingMessageAsString(@NonNull String encodedString) { + try { + return consumeIncomingMessageAsString0(encodedString); + } catch (Exception e) { + this.plugin.getLogger().warn("Unable to decode incoming messaging service message: '" + encodedString + "'", e); + return false; + } + } + + private boolean consumeIncomingMessageAsString0(@NonNull String encodedString) { Objects.requireNonNull(encodedString, "encodedString"); - JsonObject decodedObject = GsonProvider.normal().fromJson(encodedString, JsonObject.class).getAsJsonObject(); + JsonObject parsed = Objects.requireNonNull(GsonProvider.normal().fromJson(encodedString, JsonObject.class), "parsed"); + JsonObject json = parsed.getAsJsonObject(); // extract id - JsonElement idElement = decodedObject.get("id"); + JsonElement idElement = json.get("id"); if (idElement == null) { throw new IllegalStateException("Incoming message has no id argument: " + encodedString); } @@ -180,14 +190,14 @@ public boolean consumeIncomingMessageAsString(@NonNull String encodedString) { } // extract type - JsonElement typeElement = decodedObject.get("type"); + JsonElement typeElement = json.get("type"); if (typeElement == null) { throw new IllegalStateException("Incoming message has no type argument: " + encodedString); } String type = typeElement.getAsString(); // extract content - @Nullable JsonElement content = decodedObject.get("content"); + @Nullable JsonElement content = json.get("content"); // decode message Message decoded; From e0ee2c3b5fe66f557a48869c1aa9f5475badb4de Mon Sep 17 00:00:00 2001 From: Emily <35617540+emilyy-dev@users.noreply.github.com> Date: Sun, 24 Apr 2022 05:44:24 -0300 Subject: [PATCH 090/314] Resolve command entity selectors on Fabric (#3363) --- .../fabric/FabricCommandExecutor.java | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java index 8086bc3f6..3fcead276 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java @@ -26,7 +26,9 @@ package me.lucko.luckperms.fabric; import com.mojang.brigadier.Command; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; @@ -35,12 +37,16 @@ import me.lucko.luckperms.common.command.CommandManager; import me.lucko.luckperms.common.command.utils.ArgumentTokenizer; +import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.sender.Sender; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; +import net.minecraft.command.argument.EntityArgumentType; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; import java.util.List; +import java.util.ListIterator; import java.util.concurrent.CompletableFuture; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; @@ -77,10 +83,11 @@ public void register() { @Override public int run(CommandContext ctx) { - Sender wrapped = this.plugin.getSenderFactory().wrap(ctx.getSource()); + ServerCommandSource source = ctx.getSource(); + Sender wrapped = this.plugin.getSenderFactory().wrap(source); int start = ctx.getRange().getStart(); - List arguments = ArgumentTokenizer.EXECUTE.tokenizeInput(ctx.getInput().substring(start)); + List arguments = resolveSelectors(source, ArgumentTokenizer.EXECUTE.tokenizeInput(ctx.getInput().substring(start))); String label = arguments.remove(0); if (label.startsWith("/")) { @@ -93,14 +100,15 @@ public int run(CommandContext ctx) { @Override public CompletableFuture getSuggestions(CommandContext ctx, SuggestionsBuilder builder) { - Sender wrapped = this.plugin.getSenderFactory().wrap(ctx.getSource()); + ServerCommandSource source = ctx.getSource(); + Sender wrapped = this.plugin.getSenderFactory().wrap(source); int idx = builder.getStart(); String buffer = ctx.getInput().substring(idx); idx += buffer.length(); - List arguments = ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(buffer); + List arguments = resolveSelectors(source, ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(buffer)); if (!arguments.isEmpty()) { idx -= arguments.get(arguments.size() - 1).length(); } @@ -115,4 +123,42 @@ public CompletableFuture getSuggestions(CommandContext resolveSelectors(ServerCommandSource source, List args) { + if (!this.plugin.getConfiguration().get(ConfigKeys.RESOLVE_COMMAND_SELECTORS)) { + return args; + } + + // usage of @ selectors requires at least level 2 permission + ServerCommandSource atAllowedSource = source.hasPermissionLevel(2) ? source : source.withLevel(2); + for (ListIterator it = args.listIterator(); it.hasNext(); ) { + String arg = it.next(); + if (arg.isEmpty() || arg.charAt(0) != '@') { + continue; + } + + List matchedPlayers; + try { + matchedPlayers = EntityArgumentType.entities().parse(new StringReader(arg)).getPlayers(atAllowedSource); + } catch (CommandSyntaxException e) { + this.plugin.getLogger().warn("Error parsing selector '" + arg + "' for " + source + " executing " + args, e); + continue; + } + + if (matchedPlayers.isEmpty()) { + continue; + } + + if (matchedPlayers.size() > 1) { + this.plugin.getLogger().warn("Error parsing selector '" + arg + "' for " + source + " executing " + args + + ": ambiguous result (more than one player matched) - " + matchedPlayers); + continue; + } + + ServerPlayerEntity player = matchedPlayers.get(0); + it.set(player.getUuidAsString()); + } + + return args; + } + } From c35958b88ec05022270461d2b8c522c8d991a008 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 24 Apr 2022 11:40:52 +0100 Subject: [PATCH 091/314] Fix plugin message string encoding inconsistency (#3364) --- .../messaging/PluginMessageMessenger.java | 29 ++----- .../messaging/PluginMessageMessenger.java | 38 +++------ .../AbstractPluginMessageMessenger.java | 80 +++++++++++++++++++ .../messaging/PluginMessageMessenger.java | 28 +++---- .../messaging/PluginMessageMessenger.java | 58 ++++++++------ .../messaging/PluginMessageMessenger.java | 35 +++----- 6 files changed, 152 insertions(+), 116 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/messaging/pluginmsg/AbstractPluginMessageMessenger.java diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/PluginMessageMessenger.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/PluginMessageMessenger.java index 9cf47f71d..e98f8ee63 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/PluginMessageMessenger.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/PluginMessageMessenger.java @@ -26,15 +26,12 @@ package me.lucko.luckperms.bukkit.messaging; import com.google.common.collect.Iterables; -import com.google.common.io.ByteArrayDataInput; -import com.google.common.io.ByteArrayDataOutput; -import com.google.common.io.ByteStreams; import me.lucko.luckperms.bukkit.LPBukkitPlugin; +import me.lucko.luckperms.common.messaging.pluginmsg.AbstractPluginMessageMessenger; import net.luckperms.api.messenger.IncomingMessageConsumer; import net.luckperms.api.messenger.Messenger; -import net.luckperms.api.messenger.message.OutgoingMessage; import org.bukkit.entity.Player; import org.bukkit.plugin.messaging.PluginMessageListener; @@ -46,15 +43,12 @@ /** * An implementation of {@link Messenger} using the plugin messaging channels. */ -public class PluginMessageMessenger implements Messenger, PluginMessageListener { - private static final String CHANNEL = "luckperms:update"; - +public class PluginMessageMessenger extends AbstractPluginMessageMessenger implements PluginMessageListener { private final LPBukkitPlugin plugin; - private final IncomingMessageConsumer consumer; public PluginMessageMessenger(LPBukkitPlugin plugin, IncomingMessageConsumer consumer) { + super(consumer); this.plugin = plugin; - this.consumer = consumer; } public void init() { @@ -69,11 +63,7 @@ public void close() { } @Override - public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF(outgoingMessage.asEncodedString()); - byte[] data = out.toByteArray(); - + protected void sendOutgoingMessage(byte[] buf) { new BukkitRunnable() { @Override public void run() { @@ -83,21 +73,18 @@ public void run() { return; } - p.sendPluginMessage(PluginMessageMessenger.this.plugin.getLoader(), CHANNEL, data); + p.sendPluginMessage(PluginMessageMessenger.this.plugin.getLoader(), CHANNEL, buf); cancel(); } }.runTaskTimer(this.plugin.getLoader(), 1L, 100L); } @Override - public void onPluginMessageReceived(String s, @NonNull Player player, @NonNull byte[] bytes) { - if (!s.equals(CHANNEL)) { + public void onPluginMessageReceived(String channel, @NonNull Player player, byte @NonNull [] message) { + if (!channel.equals(CHANNEL)) { return; } - ByteArrayDataInput in = ByteStreams.newDataInput(bytes); - String msg = in.readUTF(); - - this.consumer.consumeIncomingMessageAsString(msg); + handleIncomingMessage(message); } } diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/PluginMessageMessenger.java b/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/PluginMessageMessenger.java index 6eee535b0..0ea601cbd 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/PluginMessageMessenger.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/PluginMessageMessenger.java @@ -25,15 +25,11 @@ package me.lucko.luckperms.bungee.messaging; -import com.google.common.io.ByteArrayDataInput; -import com.google.common.io.ByteArrayDataOutput; -import com.google.common.io.ByteStreams; - import me.lucko.luckperms.bungee.LPBungeePlugin; +import me.lucko.luckperms.common.messaging.pluginmsg.AbstractPluginMessageMessenger; import net.luckperms.api.messenger.IncomingMessageConsumer; import net.luckperms.api.messenger.Messenger; -import net.luckperms.api.messenger.message.OutgoingMessage; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; @@ -41,20 +37,15 @@ import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; -import org.checkerframework.checker.nullness.qual.NonNull; - /** * An implementation of {@link Messenger} using the plugin messaging channels. */ -public class PluginMessageMessenger implements Messenger, Listener { - private static final String CHANNEL = "luckperms:update"; - +public class PluginMessageMessenger extends AbstractPluginMessageMessenger implements Listener { private final LPBungeePlugin plugin; - private final IncomingMessageConsumer consumer; public PluginMessageMessenger(LPBungeePlugin plugin, IncomingMessageConsumer consumer) { + super(consumer); this.plugin = plugin; - this.consumer = consumer; } public void init() { @@ -70,21 +61,13 @@ public void close() { proxy.getPluginManager().unregisterListener(this); } - private void dispatchMessage(byte[] message) { + @Override + protected void sendOutgoingMessage(byte[] buf) { for (ServerInfo server : this.plugin.getBootstrap().getProxy().getServers().values()) { - server.sendData(CHANNEL, message, false); + server.sendData(CHANNEL, buf, false); } } - @Override - public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF(outgoingMessage.asEncodedString()); - - byte[] message = out.toByteArray(); - dispatchMessage(message); - } - @EventHandler public void onPluginMessage(PluginMessageEvent e) { if (!e.getTag().equals(CHANNEL)) { @@ -97,14 +80,11 @@ public void onPluginMessage(PluginMessageEvent e) { return; } - byte[] data = e.getData(); - - ByteArrayDataInput in = ByteStreams.newDataInput(data); - String msg = in.readUTF(); + byte[] buf = e.getData(); - if (this.consumer.consumeIncomingMessageAsString(msg)) { + if (handleIncomingMessage(buf)) { // Forward to other servers - this.plugin.getBootstrap().getScheduler().executeAsync(() -> dispatchMessage(data)); + this.plugin.getBootstrap().getScheduler().executeAsync(() -> sendOutgoingMessage(buf)); } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/pluginmsg/AbstractPluginMessageMessenger.java b/common/src/main/java/me/lucko/luckperms/common/messaging/pluginmsg/AbstractPluginMessageMessenger.java new file mode 100644 index 000000000..8bfb7f492 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/pluginmsg/AbstractPluginMessageMessenger.java @@ -0,0 +1,80 @@ +/* + * 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.messaging.pluginmsg; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; + +import net.luckperms.api.messenger.IncomingMessageConsumer; +import net.luckperms.api.messenger.Messenger; +import net.luckperms.api.messenger.message.OutgoingMessage; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Abstract implementation of {@link Messenger} using Minecraft's + * 'plugin messaging channels' packet. + * + *

    The {@link OutgoingMessage#asEncodedString() encoded string} format + * is used to transmit messages. {@link java.io.DataOutput#writeUTF(String)} is + * used to encode the string into raw bytes.

    + */ +public abstract class AbstractPluginMessageMessenger implements Messenger { + + /** + * The identifier of the channel used by LuckPerms for all messages. + */ + public static final String CHANNEL = "luckperms:update"; + + /** + * The {@link IncomingMessageConsumer} used by the messenger. + */ + private final IncomingMessageConsumer consumer; + + protected AbstractPluginMessageMessenger(IncomingMessageConsumer consumer) { + this.consumer = consumer; + } + + @Override + public final void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { + ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput(); + dataOutput.writeUTF(outgoingMessage.asEncodedString()); + + byte[] buf = dataOutput.toByteArray(); + + sendOutgoingMessage(buf); + } + + protected abstract void sendOutgoingMessage(byte[] buf); + + protected boolean handleIncomingMessage(byte[] buf) { + ByteArrayDataInput dataInput = ByteStreams.newDataInput(buf); + String decodedString = dataInput.readUTF(); + return this.consumer.consumeIncomingMessageAsString(decodedString); + } + +} diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/messaging/PluginMessageMessenger.java b/fabric/src/main/java/me/lucko/luckperms/fabric/messaging/PluginMessageMessenger.java index 733c8a41d..291b68fae 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/messaging/PluginMessageMessenger.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/messaging/PluginMessageMessenger.java @@ -27,6 +27,7 @@ import com.google.common.collect.Iterables; +import me.lucko.luckperms.common.messaging.pluginmsg.AbstractPluginMessageMessenger; import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask; import me.lucko.luckperms.fabric.LPFabricPlugin; @@ -34,29 +35,24 @@ import net.fabricmc.fabric.api.networking.v1.PacketSender; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.luckperms.api.messenger.IncomingMessageConsumer; -import net.luckperms.api.messenger.Messenger; -import net.luckperms.api.messenger.message.OutgoingMessage; import net.minecraft.network.PacketByteBuf; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; -import org.checkerframework.checker.nullness.qual.NonNull; - import java.util.Collection; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -public class PluginMessageMessenger implements Messenger, ServerPlayNetworking.PlayChannelHandler { - private static final Identifier CHANNEL = new Identifier("luckperms", "update"); +public class PluginMessageMessenger extends AbstractPluginMessageMessenger implements ServerPlayNetworking.PlayChannelHandler { + private static final Identifier CHANNEL = new Identifier(AbstractPluginMessageMessenger.CHANNEL); private final LPFabricPlugin plugin; - private final IncomingMessageConsumer consumer; public PluginMessageMessenger(LPFabricPlugin plugin, IncomingMessageConsumer consumer) { + super(consumer); this.plugin = plugin; - this.consumer = consumer; } public void init() { @@ -69,7 +65,7 @@ public void close() { } @Override - public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { + protected void sendOutgoingMessage(byte[] buf) { AtomicReference taskRef = new AtomicReference<>(); SchedulerTask task = this.plugin.getBootstrap().getScheduler().asyncRepeating(() -> { MinecraftServer server = this.plugin.getBootstrap().getServer().orElse(null); @@ -83,9 +79,9 @@ public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { return; } - PacketByteBuf buf = PacketByteBufs.create(); - buf.writeString(outgoingMessage.asEncodedString()); - ServerPlayNetworking.send(p, CHANNEL, buf); + PacketByteBuf packetBuf = PacketByteBufs.create(); + packetBuf.writeBytes(buf); + ServerPlayNetworking.send(p, CHANNEL, packetBuf); SchedulerTask t = taskRef.getAndSet(null); if (t != null) { @@ -96,8 +92,10 @@ public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { } @Override - public void receive(MinecraftServer server, ServerPlayerEntity entity, ServerPlayNetworkHandler netHandler, PacketByteBuf buf, PacketSender packetSender) { - String msg = buf.readString(); - this.consumer.consumeIncomingMessageAsString(msg); + public void receive(MinecraftServer server, ServerPlayerEntity entity, ServerPlayNetworkHandler netHandler, PacketByteBuf packetBuf, PacketSender packetSender) { + byte[] buf = new byte[packetBuf.readableBytes()]; + packetBuf.readBytes(buf); + + handleIncomingMessage(buf); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java index 609a70d71..f9b54d84c 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java @@ -27,13 +27,12 @@ import com.google.common.collect.Iterables; +import me.lucko.luckperms.common.messaging.pluginmsg.AbstractPluginMessageMessenger; import me.lucko.luckperms.sponge.LPSpongePlugin; import net.luckperms.api.messenger.IncomingMessageConsumer; import net.luckperms.api.messenger.Messenger; -import net.luckperms.api.messenger.message.OutgoingMessage; -import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.network.EngineConnectionSide; @@ -41,6 +40,7 @@ import org.spongepowered.api.network.channel.ChannelBuf; import org.spongepowered.api.network.channel.raw.RawDataChannel; import org.spongepowered.api.network.channel.raw.play.RawPlayDataHandler; +import org.spongepowered.api.scheduler.ScheduledTask; import org.spongepowered.api.scheduler.Task; import java.util.Collection; @@ -49,17 +49,16 @@ /** * An implementation of {@link Messenger} using the plugin messaging channels. */ -public class PluginMessageMessenger implements Messenger, RawPlayDataHandler { - private static final ResourceKey CHANNEL = ResourceKey.of("luckperms", "update"); +public class PluginMessageMessenger extends AbstractPluginMessageMessenger implements RawPlayDataHandler { + private static final ResourceKey CHANNEL = ResourceKey.resolve(AbstractPluginMessageMessenger.CHANNEL); private final LPSpongePlugin plugin; - private final IncomingMessageConsumer consumer; private RawDataChannel channel = null; public PluginMessageMessenger(LPSpongePlugin plugin, IncomingMessageConsumer consumer) { + super(consumer); this.plugin = plugin; - this.consumer = consumer; } public void init() { @@ -75,32 +74,39 @@ public void close() { } @Override - public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { - Task t = Task.builder() + protected void sendOutgoingMessage(byte[] buf) { + if (!this.plugin.getBootstrap().getGame().isServerAvailable()) { + return; + } + + Task task = Task.builder() .interval(10, TimeUnit.SECONDS) - .execute(task -> { - if (!this.plugin.getBootstrap().getGame().isServerAvailable()) { - return; - } - - Collection players = this.plugin.getBootstrap().getGame().server().onlinePlayers(); - ServerPlayer p = Iterables.getFirst(players, null); - if (p == null) { - return; - } - - this.channel.play().sendTo(p, buf -> buf.writeUTF(outgoingMessage.asEncodedString())); - task.cancel(); - }) + .execute(t -> sendOutgoingMessage(buf, t)) .plugin(this.plugin.getBootstrap().getPluginContainer()) .build(); - this.plugin.getBootstrap().getScheduler().getSyncScheduler().submit(t); + this.plugin.getBootstrap().getScheduler().getSyncScheduler().submit(task); + } + + private void sendOutgoingMessage(byte[] buf, ScheduledTask scheduledTask) { + if (!this.plugin.getBootstrap().getGame().isServerAvailable()) { + scheduledTask.cancel(); + return; + } + + Collection players = this.plugin.getBootstrap().getGame().server().onlinePlayers(); + ServerPlayer p = Iterables.getFirst(players, null); + if (p == null) { + return; + } + + this.channel.play().sendTo(p, channelBuf -> channelBuf.writeBytes(buf)); + scheduledTask.cancel(); } @Override - public void handlePayload(ChannelBuf buf, ServerSideConnection connection) { - String msg = buf.readUTF(); - this.consumer.consumeIncomingMessageAsString(msg); + public void handlePayload(ChannelBuf channelBuf, ServerSideConnection connection) { + byte[] buf = channelBuf.readBytes(channelBuf.available()); + handleIncomingMessage(buf); } } diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/messaging/PluginMessageMessenger.java b/velocity/src/main/java/me/lucko/luckperms/velocity/messaging/PluginMessageMessenger.java index df3e58987..f83b72989 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/messaging/PluginMessageMessenger.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/messaging/PluginMessageMessenger.java @@ -25,9 +25,6 @@ package me.lucko.luckperms.velocity.messaging; -import com.google.common.io.ByteArrayDataInput; -import com.google.common.io.ByteArrayDataOutput; -import com.google.common.io.ByteStreams; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent.ForwardResult; @@ -37,26 +34,23 @@ import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import com.velocitypowered.api.proxy.server.RegisteredServer; +import me.lucko.luckperms.common.messaging.pluginmsg.AbstractPluginMessageMessenger; import me.lucko.luckperms.velocity.LPVelocityPlugin; import net.luckperms.api.messenger.IncomingMessageConsumer; import net.luckperms.api.messenger.Messenger; -import net.luckperms.api.messenger.message.OutgoingMessage; - -import org.checkerframework.checker.nullness.qual.NonNull; /** * An implementation of {@link Messenger} using the plugin messaging channels. */ -public class PluginMessageMessenger implements Messenger { - private static final ChannelIdentifier CHANNEL = MinecraftChannelIdentifier.create("luckperms", "update"); +public class PluginMessageMessenger extends AbstractPluginMessageMessenger { + private static final ChannelIdentifier CHANNEL = MinecraftChannelIdentifier.from(AbstractPluginMessageMessenger.CHANNEL); private final LPVelocityPlugin plugin; - private final IncomingMessageConsumer consumer; public PluginMessageMessenger(LPVelocityPlugin plugin, IncomingMessageConsumer consumer) { + super(consumer); this.plugin = plugin; - this.consumer = consumer; } public void init() { @@ -72,21 +66,13 @@ public void close() { proxy.getEventManager().unregisterListener(this.plugin.getBootstrap(), this); } - private void dispatchMessage(byte[] message) { + @Override + protected void sendOutgoingMessage(byte[] buf) { for (RegisteredServer server : this.plugin.getBootstrap().getProxy().getAllServers()) { - server.sendPluginMessage(CHANNEL, message); + server.sendPluginMessage(CHANNEL, buf); } } - @Override - public void sendOutgoingMessage(@NonNull OutgoingMessage outgoingMessage) { - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF(outgoingMessage.asEncodedString()); - - byte[] message = out.toByteArray(); - dispatchMessage(message); - } - @Subscribe public void onPluginMessage(PluginMessageEvent e) { // compare the underlying text representation of the channel @@ -102,12 +88,11 @@ public void onPluginMessage(PluginMessageEvent e) { return; } - ByteArrayDataInput in = e.dataAsDataStream(); - String msg = in.readUTF(); + byte[] buf = e.getData(); - if (this.consumer.consumeIncomingMessageAsString(msg)) { + if (handleIncomingMessage(buf)) { // Forward to other servers - this.plugin.getBootstrap().getScheduler().executeAsync(() -> dispatchMessage(e.getData())); + this.plugin.getBootstrap().getScheduler().executeAsync(() -> sendOutgoingMessage(buf)); } } } From e6cc817436e37d1128b6ba3c662048626cfb7627 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 15 May 2022 18:22:21 +0100 Subject: [PATCH 092/314] Update nexus mirror url --- .../common/dependencies/DependencyRepository.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRepository.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRepository.java index 3606a2906..7dcdd4ea5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRepository.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRepository.java @@ -44,16 +44,19 @@ public enum DependencyRepository { /** * Maven Central mirror repository. * - *

    This is used to reduce the load on repo.maven.org - I'm told they - * don't like being used as a CDN.

    + *

    This is used to reduce the load on repo.maven.org.

    + * + *

    Although Maven Central is technically a CDN, it is meant for developer use, + * not end-user products. It is trivial and not very expensive for us to provide a + * mirror, which will absorb any traffic caused by LP.

    + * + *

    LuckPerms will fallback to the real-thing if the mirror ever goes offline. + * Retrieved content is validated with a checksum, so there is no risk to integrity.

    */ - // Please ask me (@lucko) before using this mirror in your own project. - LUCK_MIRROR("https://nexus.lucko.me/repository/maven-central/") { + MAVEN_CENTRAL_MIRROR("https://libraries.luckperms.net/") { @Override protected URLConnection openConnection(Dependency dependency) throws IOException { URLConnection connection = super.openConnection(dependency); - - // Tell nexus who we are connection.setRequestProperty("User-Agent", "luckperms"); // Set a connect/read timeout, so if the mirror goes offline we can fallback From 65a4c677454d0983da6fb14d84ff57c84fe17eb5 Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 19 May 2022 18:12:42 +0100 Subject: [PATCH 093/314] Fix bug in Sponge PermissionServiceProxy --- .../sponge/service/proxy/api8/PermissionServiceProxy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionServiceProxy.java b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionServiceProxy.java index 0c5648a62..10629063f 100644 --- a/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionServiceProxy.java +++ b/sponge/sponge-service-api8/src/main/java/me/lucko/luckperms/sponge/service/proxy/api8/PermissionServiceProxy.java @@ -151,7 +151,7 @@ public Set contexts() { @Override public Set contextsFor(Cause cause) { - return CompatibilityUtil.convertContexts(this.handle.getContextsForCurrentCause()); + return CompatibilityUtil.convertContexts(this.handle.getContextsForCause(cause)); } @Override From 6421525f3ab20252549b10f4ea51fc0ac1b8bbf1 Mon Sep 17 00:00:00 2001 From: Alex Thomson Date: Sat, 21 May 2022 07:09:04 +1200 Subject: [PATCH 094/314] LuckPerms for Forge (#3262) --- CONTRIBUTING.md | 2 +- README.md | 2 +- .../net/luckperms/api/platform/Platform.java | 3 +- build.gradle | 1 + bukkit/build.gradle | 1 - common/build.gradle | 1 + .../common/loader/LoaderBootstrap.java | 4 +- .../command/BrigadierCommandExecutor.java | 114 ++++ .../command/access/CommandPermission.java | 13 +- .../luckperms/common/config/ConfigKeys.java | 4 +- .../plugin/AbstractLuckPermsPlugin.java | 5 +- .../plugin/bootstrap/LuckPermsBootstrap.java | 10 +- .../fabric/FabricCalculatorFactory.java | 2 +- .../fabric/FabricCommandExecutor.java | 61 +- forge/build.gradle | 75 ++ forge/forge-api/build.gradle | 29 + .../forge/capabilities/UserCapability.java | 85 +++ forge/gradle.properties | 2 + forge/loader/build.gradle | 78 +++ .../forge/loader/ForgeLoaderPlugin.java | 100 +++ .../src/main/resources/META-INF/mods.toml | 20 + forge/loader/src/main/resources/luckperms.png | Bin 0 -> 36936 bytes forge/loader/src/main/resources/pack.mcmeta | 6 + .../luckperms/forge/ForgeCommandExecutor.java | 110 +++ .../luckperms/forge/ForgeConfigAdapter.java | 47 ++ .../lucko/luckperms/forge/ForgeEventBus.java | 50 ++ .../forge/ForgeSchedulerAdapter.java | 45 ++ .../luckperms/forge/ForgeSenderFactory.java | 116 ++++ .../luckperms/forge/LPForgeBootstrap.java | 290 ++++++++ .../lucko/luckperms/forge/LPForgePlugin.java | 249 +++++++ .../calculator/ForgeCalculatorFactory.java | 78 +++ .../calculator/ServerOwnerProcessor.java | 52 ++ .../capabilities/UserCapabilityImpl.java | 149 ++++ .../capabilities/UserCapabilityListener.java | 99 +++ .../forge/context/ForgeContextManager.java | 80 +++ .../forge/context/ForgePlayerCalculator.java | 149 ++++ .../forge/listeners/ForgeAutoOpListener.java | 98 +++ .../listeners/ForgeCommandListUpdater.java | 121 ++++ .../listeners/ForgeConnectionListener.java | 154 +++++ .../listeners/ForgePlatformListener.java | 94 +++ .../messaging/ForgeMessagingFactory.java | 72 ++ .../messaging/PluginMessageMessenger.java | 101 +++ .../forge/service/ForgePermissionHandler.java | 155 +++++ .../ForgePermissionHandlerListener.java | 65 ++ .../forge/util/BrigadierInjector.java | 186 +++++ .../forge/util/ForgeEventBusFacade.java | 265 ++++++++ forge/src/main/resources/luckperms.conf | 640 ++++++++++++++++++ gradle.properties | 4 +- settings.gradle | 3 + 49 files changed, 4016 insertions(+), 74 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/command/BrigadierCommandExecutor.java create mode 100644 forge/build.gradle create mode 100644 forge/forge-api/build.gradle create mode 100644 forge/forge-api/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapability.java create mode 100644 forge/gradle.properties create mode 100644 forge/loader/build.gradle create mode 100644 forge/loader/src/main/java/me/lucko/luckperms/forge/loader/ForgeLoaderPlugin.java create mode 100644 forge/loader/src/main/resources/META-INF/mods.toml create mode 100644 forge/loader/src/main/resources/luckperms.png create mode 100644 forge/loader/src/main/resources/pack.mcmeta create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/ForgeCommandExecutor.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/ForgeConfigAdapter.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/ForgeEventBus.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/ForgeSchedulerAdapter.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/ForgeSenderFactory.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/LPForgeBootstrap.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/LPForgePlugin.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/calculator/ForgeCalculatorFactory.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/calculator/ServerOwnerProcessor.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapabilityImpl.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapabilityListener.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/context/ForgeContextManager.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/context/ForgePlayerCalculator.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeAutoOpListener.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeCommandListUpdater.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeConnectionListener.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgePlatformListener.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/messaging/ForgeMessagingFactory.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/messaging/PluginMessageMessenger.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandlerListener.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/util/BrigadierInjector.java create mode 100644 forge/src/main/java/me/lucko/luckperms/forge/util/ForgeEventBusFacade.java create mode 100644 forge/src/main/resources/luckperms.conf diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 30037813a..f55363882 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,4 +23,4 @@ The project is split up into a few separate modules. * **API** - The public, semantically versioned API used by other plugins wishing to integrate with and retrieve data from LuckPerms. This module (for the most part) does not contain any implementation itself, and is provided by the plugin. * **Common** - The common module contains most of the code which implements the respective LuckPerms plugins. This abstract module reduces duplicated code throughout the project. -* **Bukkit, BungeeCord, Sponge, Nukkit, Velocity & Fabric** - Each use the common module to implement plugins on the respective server platforms. +* **Bukkit, BungeeCord, Fabric, Forge, Nukkit, Sponge & Velocity** - Each use the common module to implement plugins on the respective server platforms. diff --git a/README.md b/README.md index 56ac8b795..2d266a359 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ The project is split up into a few separate modules. * **API** - The public, semantically versioned API used by other plugins wishing to integrate with and retrieve data from LuckPerms. This module (for the most part) does not contain any implementation itself, and is provided by the plugin. * **Common** - The common module contains most of the code which implements the respective LuckPerms plugins. This abstract module reduces duplicated code throughout the project. -* **Bukkit, BungeeCord, Sponge, Fabric, Nukkit & Velocity** - Each use the common module to implement plugins on the respective server platforms. +* **Bukkit, BungeeCord, Fabric, Forge, Nukkit, Sponge & Velocity** - Each use the common module to implement plugins on the respective server platforms. ## License LuckPerms is licensed under the permissive MIT license. Please see [`LICENSE.txt`](https://github.com/lucko/LuckPerms/blob/master/LICENSE.txt) for more info. diff --git a/api/src/main/java/net/luckperms/api/platform/Platform.java b/api/src/main/java/net/luckperms/api/platform/Platform.java index 9ed38beee..92b4fd09f 100644 --- a/api/src/main/java/net/luckperms/api/platform/Platform.java +++ b/api/src/main/java/net/luckperms/api/platform/Platform.java @@ -75,7 +75,8 @@ enum Type { SPONGE("Sponge"), NUKKIT("Nukkit"), VELOCITY("Velocity"), - FABRIC("Fabric"); + FABRIC("Fabric"), + FORGE("Forge"); private final String friendlyName; diff --git a/build.gradle b/build.gradle index 4a17698b8..44022515d 100644 --- a/build.gradle +++ b/build.gradle @@ -54,5 +54,6 @@ subprojects { mavenCentral() maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } maven { url 'https://repo.lucko.me/' } + maven { url 'https://libraries.minecraft.net/' } } } diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 7dcf52230..fcdbe4ef4 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -4,7 +4,6 @@ plugins { repositories { maven { url 'https://papermc.io/repo/repository/maven-public/' } - maven { url 'https://libraries.minecraft.net/' } } dependencies { diff --git a/common/build.gradle b/common/build.gradle index b2137a5f0..374a0ade5 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -17,6 +17,7 @@ dependencies { compileOnly project(':common:loader-utils') + compileOnly 'com.mojang:brigadier:1.0.18' compileOnly 'org.slf4j:slf4j-api:1.7.30' compileOnly 'org.apache.logging.log4j:log4j-api:2.14.0' diff --git a/common/loader-utils/src/main/java/me/lucko/luckperms/common/loader/LoaderBootstrap.java b/common/loader-utils/src/main/java/me/lucko/luckperms/common/loader/LoaderBootstrap.java index 875a2b153..910a51f01 100644 --- a/common/loader-utils/src/main/java/me/lucko/luckperms/common/loader/LoaderBootstrap.java +++ b/common/loader-utils/src/main/java/me/lucko/luckperms/common/loader/LoaderBootstrap.java @@ -32,8 +32,8 @@ public interface LoaderBootstrap { void onLoad(); - void onEnable(); + default void onEnable() {} - void onDisable(); + default void onDisable() {} } diff --git a/common/src/main/java/me/lucko/luckperms/common/command/BrigadierCommandExecutor.java b/common/src/main/java/me/lucko/luckperms/common/command/BrigadierCommandExecutor.java new file mode 100644 index 000000000..3ecb146ff --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/command/BrigadierCommandExecutor.java @@ -0,0 +1,114 @@ +/* + * 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.command; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import me.lucko.luckperms.common.command.utils.ArgumentTokenizer; +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.sender.Sender; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public abstract class BrigadierCommandExecutor extends CommandManager implements Command, SuggestionProvider { + + protected static final String[] COMMAND_ALIASES = new String[]{"luckperms", "lp", "perm", "perms", "permission", "permissions"}; + + private final LuckPermsPlugin plugin; + + protected BrigadierCommandExecutor(LuckPermsPlugin plugin) { + super(plugin); + this.plugin = plugin; + } + + public abstract Sender getSender(S source); + + public abstract List resolveSelectors(S source, List args); + + @Override + public int run(CommandContext context) throws CommandSyntaxException { + S source = context.getSource(); + Sender sender = getSender(source); + + int start = context.getRange().getStart(); + String buffer = context.getInput().substring(start); + + List arguments; + if (this.plugin.getConfiguration().get(ConfigKeys.RESOLVE_COMMAND_SELECTORS)) { + arguments = resolveSelectors(source, ArgumentTokenizer.EXECUTE.tokenizeInput(buffer)); + } else { + arguments = ArgumentTokenizer.EXECUTE.tokenizeInput(buffer); + } + + String label = arguments.remove(0); + if (label.startsWith("/")) { + label = label.substring(1); + } + + executeCommand(sender, label, arguments); + return Command.SINGLE_SUCCESS; + } + + @Override + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException { + S source = context.getSource(); + Sender sender = getSender(source); + + int idx = builder.getStart(); + + String buffer = builder.getInput().substring(idx); + idx += buffer.length(); + + List arguments = ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(buffer); + List resolvedArguments; + if (this.plugin.getConfiguration().get(ConfigKeys.RESOLVE_COMMAND_SELECTORS)) { + resolvedArguments = resolveSelectors(source, new ArrayList<>(arguments)); + } else { + resolvedArguments = arguments; + } + + if (!arguments.isEmpty() && !resolvedArguments.isEmpty()) { + idx -= arguments.get(arguments.size() - 1).length(); + } + + List completions = tabCompleteCommand(sender, resolvedArguments); + + // Offset the builder from the current string range so suggestions are placed in the right spot + builder = builder.createOffset(idx); + for (String completion : completions) { + builder.suggest(completion); + } + return builder.buildFuture(); + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/command/access/CommandPermission.java b/common/src/main/java/me/lucko/luckperms/common/command/access/CommandPermission.java index dfb5efc3a..ed1ed15e3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/access/CommandPermission.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/access/CommandPermission.java @@ -173,6 +173,7 @@ public enum CommandPermission { public static final String ROOT = "luckperms."; private final String node; + private final String permission; private final Type type; @@ -180,16 +181,22 @@ public enum CommandPermission { this.type = type; if (type == Type.NONE) { - this.node = ROOT + node; + this.node = node; } else { - this.node = ROOT + type.getTag() + "." + node; + this.node = type.getTag() + "." + node; } + + this.permission = ROOT + this.node; } - public String getPermission() { + public String getNode() { return this.node; } + public String getPermission() { + return this.permission; + } + public boolean isAuthorized(Sender sender) { return sender.hasPermission(this); } diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 2c5ac5a31..28cc6c1ad 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -505,9 +505,9 @@ private ConfigKeys() {} public static final ConfigKey VAULT_IGNORE_WORLD = booleanKey("vault-ignore-world", false); /** - * If the owner of an integrated server should automatically bypasses all permission checks. On fabric, this only applies on an Integrated Server. + * If the owner of an integrated server should automatically bypass all permission checks. On fabric and forge, this only applies on an Integrated Server. */ - public static final ConfigKey FABRIC_INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS = booleanKey("integrated-server-owner-bypasses-checks", true); + public static final ConfigKey INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS = booleanKey("integrated-server-owner-bypasses-checks", true); /** * Disabled context calculators diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 24ed279c7..0650fcb47 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -117,8 +117,12 @@ public final void load() { this.dependencyManager = new DependencyManager(this); this.dependencyManager.loadDependencies(getGlobalDependencies()); + // load translations this.translationManager = new TranslationManager(this); this.translationManager.reload(); + + // load some utilities early + this.permissionRegistry = new PermissionRegistry(getBootstrap().getScheduler()); } public final void enable() { @@ -130,7 +134,6 @@ public final void enable() { // load some utilities early this.verboseHandler = new VerboseHandler(getBootstrap().getScheduler()); - this.permissionRegistry = new PermissionRegistry(getBootstrap().getScheduler()); this.logDispatcher = new LogDispatcher(this); // load configuration diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/LuckPermsBootstrap.java b/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/LuckPermsBootstrap.java index 2680ab00e..df8fbcb53 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/LuckPermsBootstrap.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/LuckPermsBootstrap.java @@ -135,10 +135,12 @@ public interface LuckPermsBootstrap { /** * Gets the plugins main data storage directory * - *

    Bukkit: /root/plugins/LuckPerms

    - *

    Bungee: /root/plugins/LuckPerms

    - *

    Sponge: /root/luckperms/

    - *

    Fabric: /root/mods/LuckPerms

    + *

    Bukkit: ./plugins/LuckPerms

    + *

    BungeeCord: ./plugins/LuckPerms

    + *

    Sponge: ./luckperms/

    + *

    Velocity: ./plugins/luckperms

    + *

    Fabric: ./mods/LuckPerms

    + *

    Forge: ./config/luckperms

    * * @return the platforms data folder */ diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCalculatorFactory.java b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCalculatorFactory.java index 00080670a..6fca4aceb 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCalculatorFactory.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCalculatorFactory.java @@ -68,7 +68,7 @@ public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metad } boolean integratedOwner = queryOptions.option(FabricContextManager.INTEGRATED_SERVER_OWNER).orElse(false); - if (integratedOwner && this.plugin.getConfiguration().get(ConfigKeys.FABRIC_INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS)) { + if (integratedOwner && this.plugin.getConfiguration().get(ConfigKeys.INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS)) { processors.add(ServerOwnerProcessor.INSTANCE); } diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java index 3fcead276..f7b42aa97 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java @@ -25,21 +25,12 @@ package me.lucko.luckperms.fabric; -import com.mojang.brigadier.Command; import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.suggestion.SuggestionProvider; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; - -import me.lucko.luckperms.common.command.CommandManager; -import me.lucko.luckperms.common.command.utils.ArgumentTokenizer; -import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.command.BrigadierCommandExecutor; import me.lucko.luckperms.common.sender.Sender; - import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.minecraft.command.argument.EntityArgumentType; import net.minecraft.server.command.ServerCommandSource; @@ -47,14 +38,12 @@ import java.util.List; import java.util.ListIterator; -import java.util.concurrent.CompletableFuture; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; -public class FabricCommandExecutor extends CommandManager implements Command, SuggestionProvider { - private static final String[] COMMAND_ALIASES = new String[] {"luckperms", "lp", "perm", "perms", "permission", "permissions"}; +public class FabricCommandExecutor extends BrigadierCommandExecutor { private final LPFabricPlugin plugin; @@ -82,52 +71,12 @@ public void register() { } @Override - public int run(CommandContext ctx) { - ServerCommandSource source = ctx.getSource(); - Sender wrapped = this.plugin.getSenderFactory().wrap(source); - - int start = ctx.getRange().getStart(); - List arguments = resolveSelectors(source, ArgumentTokenizer.EXECUTE.tokenizeInput(ctx.getInput().substring(start))); - - String label = arguments.remove(0); - if (label.startsWith("/")) { - label = label.substring(1); - } - - executeCommand(wrapped, label, arguments); - return Command.SINGLE_SUCCESS; + public Sender getSender(ServerCommandSource source) { + return this.plugin.getSenderFactory().wrap(source); } @Override - public CompletableFuture getSuggestions(CommandContext ctx, SuggestionsBuilder builder) { - ServerCommandSource source = ctx.getSource(); - Sender wrapped = this.plugin.getSenderFactory().wrap(source); - - int idx = builder.getStart(); - - String buffer = ctx.getInput().substring(idx); - idx += buffer.length(); - - List arguments = resolveSelectors(source, ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(buffer)); - if (!arguments.isEmpty()) { - idx -= arguments.get(arguments.size() - 1).length(); - } - - List completions = tabCompleteCommand(wrapped, arguments); - - // Offset the builder from the current string range so suggestions are placed in the right spot - builder = builder.createOffset(idx); - for (String completion : completions) { - builder.suggest(completion); - } - return builder.buildFuture(); - } - - private List resolveSelectors(ServerCommandSource source, List args) { - if (!this.plugin.getConfiguration().get(ConfigKeys.RESOLVE_COMMAND_SELECTORS)) { - return args; - } - + public List resolveSelectors(ServerCommandSource source, List args) { // usage of @ selectors requires at least level 2 permission ServerCommandSource atAllowedSource = source.hasPermissionLevel(2) ? source : source.withLevel(2); for (ListIterator it = args.listIterator(); it.hasNext(); ) { diff --git a/forge/build.gradle b/forge/build.gradle new file mode 100644 index 000000000..4b12a22dd --- /dev/null +++ b/forge/build.gradle @@ -0,0 +1,75 @@ +buildscript { + repositories { + maven { url 'https://plugins.gradle.org/m2' } + maven { url 'https://maven.minecraftforge.net/' } + } + + dependencies { + classpath 'gradle.plugin.com.github.johnrengelman:shadow:7.1.2' + classpath 'net.kyori:blossom:1.3.0' + classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+' + } +} + +apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'net.kyori.blossom' +apply plugin: 'net.minecraftforge.gradle' + +sourceCompatibility = 1.8 +targetCompatibility = 17 + +blossom { + replaceTokenIn 'src/main/java/me/lucko/luckperms/forge/LPForgeBootstrap.java' + replaceToken '@version@', project.ext.fullVersion +} + +minecraft { + mappings channel: 'official', version: minecraftVersion +} + +configurations { +} + +repositories { +} + +dependencies { + minecraft "net.minecraftforge:forge:${minecraftVersion}-${forgeVersion}" + implementation project(':common') + compileOnly project(':common:loader-utils') + compileOnly project(':forge:forge-api') +} + +reobf { + shadowJar {} +} + +shadowJar { + archiveFileName = "luckperms-forge.jarinjar" + + dependencies { + include(dependency('me.lucko.luckperms:.*')) + } + + relocate 'net.kyori.adventure', 'me.lucko.luckperms.lib.adventure' + relocate 'net.kyori.event', 'me.lucko.luckperms.lib.eventbus' + relocate 'com.github.benmanes.caffeine', 'me.lucko.luckperms.lib.caffeine' + relocate 'okio', 'me.lucko.luckperms.lib.okio' + relocate 'okhttp3', 'me.lucko.luckperms.lib.okhttp3' + relocate 'net.bytebuddy', 'me.lucko.luckperms.lib.bytebuddy' + relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore' + relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb' + relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql' + relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql' + relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari' + relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb' + relocate 'org.bson', 'me.lucko.luckperms.lib.bson' + relocate 'redis.clients.jedis', 'me.lucko.luckperms.lib.jedis' + relocate 'com.rabbitmq', 'me.lucko.luckperms.lib.rabbitmq' + relocate 'org.apache.commons.pool2', 'me.lucko.luckperms.lib.commonspool2' + relocate 'ninja.leaping.configurate', 'me.lucko.luckperms.lib.configurate' +} + +artifacts { + archives shadowJar +} diff --git a/forge/forge-api/build.gradle b/forge/forge-api/build.gradle new file mode 100644 index 000000000..8edccffbe --- /dev/null +++ b/forge/forge-api/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + maven { url 'https://maven.minecraftforge.net/' } + } + + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+' + } +} + +apply plugin: 'net.minecraftforge.gradle' + +sourceCompatibility = 1.8 +targetCompatibility = 17 + +minecraft { + mappings channel: 'official', version: minecraftVersion +} + +configurations { +} + +repositories { +} + +dependencies { + minecraft "net.minecraftforge:forge:${minecraftVersion}-${forgeVersion}" + implementation project(':api') +} diff --git a/forge/forge-api/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapability.java b/forge/forge-api/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapability.java new file mode 100644 index 000000000..9a1323837 --- /dev/null +++ b/forge/forge-api/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapability.java @@ -0,0 +1,85 @@ +/* + * 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.forge.capabilities; + +import net.luckperms.api.query.QueryOptions; +import net.luckperms.api.util.Tristate; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.common.capabilities.CapabilityToken; + +/** + * A Forge {@link Capability} that attaches LuckPerms functionality onto {@link ServerPlayer}s. + */ +public interface UserCapability { + + /** + * The identifier used for the capability + */ + ResourceLocation IDENTIFIER = new ResourceLocation("luckperms", "user"); + + /** + * The capability instance. + */ + Capability CAPABILITY = CapabilityManager.get(new CapabilityToken(){}); + + /** + * Checks for a permission. + * + * @param permission the permission + * @return the result + */ + default boolean hasPermission(String permission) { + return checkPermission(permission).asBoolean(); + } + + /** + * Runs a permission check. + * + * @param permission the permission + * @return the result + */ + Tristate checkPermission(String permission); + + /** + * Runs a permission check. + * + * @param permission the permission + * @param queryOptions the query options + * @return the result + */ + Tristate checkPermission(String permission, QueryOptions queryOptions); + + /** + * Gets the user's currently query options. + * + * @return the current query options for the user + */ + QueryOptions getQueryOptions(); + +} diff --git a/forge/gradle.properties b/forge/gradle.properties new file mode 100644 index 000000000..7c8b2904a --- /dev/null +++ b/forge/gradle.properties @@ -0,0 +1,2 @@ +minecraftVersion=1.18.2 +forgeVersion=40.1.20 \ No newline at end of file diff --git a/forge/loader/build.gradle b/forge/loader/build.gradle new file mode 100644 index 000000000..c66d7276b --- /dev/null +++ b/forge/loader/build.gradle @@ -0,0 +1,78 @@ +buildscript { + repositories { + maven { url 'https://plugins.gradle.org/m2' } + maven { url 'https://maven.minecraftforge.net/' } + } + + dependencies { + classpath 'gradle.plugin.com.github.johnrengelman:shadow:7.1.2' + classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+' + } +} + +apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'java-library' +apply plugin: 'net.minecraftforge.gradle' + +sourceCompatibility = 1.8 +targetCompatibility = 17 + +minecraft { + mappings channel: 'official', version: minecraftVersion +} + +repositories { +} + +dependencies { + minecraft "net.minecraftforge:forge:${minecraftVersion}-${forgeVersion}" + implementation project(':api') + implementation project(':common:loader-utils') + implementation project(':forge:forge-api') +} + +build { + dependsOn(":forge:build") + dependsOn(":forge-api:build") +} + +jar { + manifest { + attributes( + 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'Implementation-Title': 'LuckPerms', + 'Implementation-Vendor': 'LuckPerms', + 'Implementation-Version': project.ext.fullVersion, + 'Specification-Title': 'luckperms', + 'Specification-Vendor': 'LuckPerms', + 'Specification-Version': '1' + ) + } +} + +processResources { + filesMatching('META-INF/mods.toml') { + expand 'version': project.ext.fullVersion + } +} + +reobf { + shadowJar {} +} + +shadowJar { + archiveFileName = "LuckPerms-Forge-${project.ext.fullVersion}.jar" + + from { + project(':forge').tasks.shadowJar.archiveFile + } + + dependencies { + include(dependency('net.luckperms:.*')) + include(dependency('me.lucko.luckperms:.*')) + } +} + +artifacts { + archives shadowJar +} diff --git a/forge/loader/src/main/java/me/lucko/luckperms/forge/loader/ForgeLoaderPlugin.java b/forge/loader/src/main/java/me/lucko/luckperms/forge/loader/ForgeLoaderPlugin.java new file mode 100644 index 000000000..951311603 --- /dev/null +++ b/forge/loader/src/main/java/me/lucko/luckperms/forge/loader/ForgeLoaderPlugin.java @@ -0,0 +1,100 @@ +/* + * 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.forge.loader; + +import me.lucko.luckperms.common.loader.JarInJarClassLoader; +import me.lucko.luckperms.common.loader.LoaderBootstrap; + +import net.minecraftforge.fml.IExtensionPoint; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.loading.FMLEnvironment; +import net.minecraftforge.network.NetworkConstants; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.lang.reflect.InvocationTargetException; +import java.util.function.Supplier; + +@Mod(value = "luckperms") +public class ForgeLoaderPlugin implements Supplier { + private static final Logger LOGGER = LogManager.getLogger("luckperms"); + + private static final String JAR_NAME = "luckperms-forge.jarinjar"; + private static final String BOOTSTRAP_CLASS = "me.lucko.luckperms.forge.LPForgeBootstrap"; + + private final ModContainer container; + + private JarInJarClassLoader loader; + private LoaderBootstrap plugin; + + public ForgeLoaderPlugin() { + this.container = ModList.get().getModContainerByObject(this).orElse(null); + + markAsNotRequiredClientSide(); + + if (FMLEnvironment.dist.isClient()) { + LOGGER.info("Skipping LuckPerms init (not supported on the client!)"); + return; + } + + this.loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onCommonSetup); + } + + @Override + public ModContainer get() { + return this.container; + } + + public void onCommonSetup(FMLCommonSetupEvent event) { + this.plugin = this.loader.instantiatePlugin(BOOTSTRAP_CLASS, Supplier.class, this); + this.plugin.onLoad(); + } + + private static void markAsNotRequiredClientSide() { + try { + // workaround as we don't compile against java 17 + ModLoadingContext.class.getDeclaredMethod("registerExtensionPoint", Class.class, Supplier.class) + .invoke( + ModLoadingContext.get(), + IExtensionPoint.DisplayTest.class, + (Supplier) () -> new IExtensionPoint.DisplayTest( + () -> NetworkConstants.IGNORESERVERONLY, + (a, b) -> true + ) + ); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/forge/loader/src/main/resources/META-INF/mods.toml b/forge/loader/src/main/resources/META-INF/mods.toml new file mode 100644 index 000000000..b773ff9c8 --- /dev/null +++ b/forge/loader/src/main/resources/META-INF/mods.toml @@ -0,0 +1,20 @@ +modLoader="javafml" +loaderVersion="[40,)" +license="MIT" +issueTrackerURL="https://github.com/LuckPerms/LuckPerms/issues" + +[[mods]] + modId="luckperms" + version="${version}" + displayName="LuckPerms" + displayURL="https://luckperms.net/" + logoFile="luckperms.png" + credits="Luck" + authors="Luck" + description="A permissions plugin for Minecraft servers." +[[dependencies.luckperms]] + modId="forge" + mandatory=true + versionRange="[40.1.20,)" + ordering="NONE" + side="BOTH" \ No newline at end of file diff --git a/forge/loader/src/main/resources/luckperms.png b/forge/loader/src/main/resources/luckperms.png new file mode 100644 index 0000000000000000000000000000000000000000..2e0ea669a550f54667a8a3c2e7eb23e11e4d45bc GIT binary patch literal 36936 zcmX_o1yodR)b#}Ix*Mds8>AZ?QW}Pm8tG02=?(#DQ5vM1fftbO5DAfz{x09Q z{-3pET)^D>+$YZ2XYYL`Mq5(}2a_BV000~nW%<_tfDHZ@8NfgTU(Wshy8&OGyDJ-e z0RRf(=^xUk*qA2phh#7XBbct69n9C-(-!dc_2qJOb@GB*yW4WPdD>?mi<1MuOF%_l zM$a$jaK+cp%rIB#;i_fkw!(Lz!Dh7CS%to(F9yo4>BjJ^-NssOlQo|9%VbOH~Y_CPl;Tm*zxM_A$f+=boztWaqi@A|GyAt z$K5Z8F^T*DHWXUyJ zS0#e<$>PeF7tpP}ZF>oS27eCd05gEy#64NyO2`%v8edNf^in6;JIvRZpKgb|lT~iz zK_Foa+ezlvO6$*hD(JB3>3`89D$Vh-z%9U*riF)kJvt(I;Dufxm%IZv`uay(Ilk<% z(@s2)8xvDgrDygG3{E;o(#qsV%PlkRn|(2=3Oa8g(Amn$Ii7(^r2l{lG%G?%ohBY= zb(~i)rZnkZyPD1i-JG+U%gak@pmHA`zE4aHgqK85?y;lRrF&VRTXDigfrL-LQ<>vP zQKwMXG2q2OqWeTrjmg)8e_<~qKjBd%;2jks!;=->y1KsV-#XQgEfT1zTpWWqp{@5W z{}WT`g22;q&I|^O*!S9J9R#SIU0ibUsp9cC3&D3`{cQ0n-}raB<%EOQYd3Mt7+d7L znN!=dye&$XB)t3XIryZPOaw8cqGDo6p^=8?Z=d3Ao|wIj}rQD((nHwKcJ31XC9P>I5nVJ~H;V=#dt zF-}g-xtN%kFO`+6V~{pt>C%05`LtFf@yf~r8ZR#|n(|eoA|X8h&-`bTqpk%5K}!Vh zB@41=B1a<9HcF^K%4;>XIFG=tx8`% zNNDda2=J8Oqh$z_1%f9ax0B7y&A13_WuGE&dG*ZAa~Ev*R*=_&KAF~PDK|Tz{z1te zzwFMF2>4Odn|vsP3Jhc9Pa2MhJOaDq56B3AjFc%n=plM+(H-*M{6il9_cQR_c8&zlSEN3-aUAylCaIQYuBxu5NxmP3q0BMU6F}vAyHA$ z;HZ`vtmi6ZY=H8}f3vfh<9269m6gF+yv-)yqrtWX;BEmOtGBqFq!(1&kY7TU*T<7w&i`uA`9ro*p~ynaJLnf&N8y z6fPAxDOIEcAu(Wsc;&MAfiyu;3VXd6XfHa$OYJX%K-Ur!i?0y){WkUB;$zJ%FX!Yd zGe|yLcb%e=qUFon%z5;sOZm6g8}$NR9=xA4D$r8V+A3O(aJYL3J)9|s%*V^r;2r3! zVi_?sH8o`h|Hu$w8{iNig?DW+AM>lJzYQK?@QEUQG2HVXX4sJK}T|R zZtl_^VH_hG*9KhiawG6kUxf%juzkNZH|G&hg-0u)N~b$HIIuwDgUGtY zkXVY&k|X$+^o*80j8>i-nRytQdxemX2Oh6cu>rXK-PR4`M|4h26+EAIA69Jgf8!^P zLjWKKA}7vwMG_rBT=5(YPAe*L<$KB^$6(XvJOm%wW)D&9J#R*Q!hpLObEMa{|M`KH z2ClzH1u*m)+>T}Pbf-vwCGt8!Zq%4HcHQ2n24T~ ziWDdqAhVNG9E<$Wf^0nvA1Zl1Q>+*sIp+0X$+#e!yUO;qy(l7ouKIGMU&!iRn$&tpfEm9taF z={PS(?UCd!+IvT*Jn)rw;Mmp4DL@g1mujpB*re?4?jC@H)(6DEE261yxDyW~>*C8; zctb>p8;3qeck4=GDdx8eeIgU0{pr->S4&K9ZV-QE!n+Dppi|^Fh`iI$pG|`K_~hYJ zh1;M7H8ga$96tmI@8v_|jTQC~^l|*}?+$o8Gon)3#6y=yc-o2aS60XERp|zJR-rw9 zNjT6IlK9-O*4rO&Qdkc-mD70OA%EX>Ja#(G*L>gSVd$s;rdkFD1{8ru)B)VZ;-@yc^T>N(ssBek0-vAxom=u4)e%s7YYLD|MoQf#7v1tX=voA0BM-Mr|? zgOrft2E>%i-_4w}$L*Yuvc^U)6Bs1*-T89_Ew~jEydLC$DtUVZ-2l9uxRJ#zOWuEr z2=na^o=^IS`XM@oBFsq5#i_Mp`4btTfeE9}QC7b+`3H{Qf=eI^4$A}_mNFDtOxoQA z{1clx@zLR^d8RtARak9}Njy#~{P#@}eF{-luE3i=btn?I=KGG`^aMqk^^AT~&ab*c z);fcVi^7usD^QeWKKT9H&46ep02l#>gIorH6Z1iEOz@`Pdqx`@7WWC~`v}jwf!GIJ z=65f?;|p7(m*eI|VDbz|&VuJl7a^nk7o6ztUSiilG~F6SjYFEe?jHm-3lFBB>$zg| z9F(a{Jj2j}AOgB0X4)hm`>?zvq!hEh6ELCNf`I@`D70!yComlF{Q2=i52Jv;^7bjj zy27z`N&3cPl(2gPyoKIo(F=zqE1OU-=?q z+U#6EJ~kGZz+JfK`@|!*PqQa5pYTr22)fhyA2I$%&r+l&6Fv?X@rBPY6Lu0y11|9 zOhCnDwS7TCOzf5>zFth;9zTXvT0s}1s+LR+e2JesAdESECZ|<}#Y{b^a3Vu`#0}ha z8F5@%XT4+r0}O+nrKKep+vR|{*a^DWp1%6chL_~4e8_Is)%(KX9413mATw*k?tVJ9qF#IpDQ^;c?1hbo7G!DbSm-4G&Lh*5#&QC9gYidO2 zf(pH+cuE+3(zdnT7=vgc3+=$qfThL7U`|mCe^g>1^$GG7R#p9G&pFeg#R}m*-o!X? zC6_zWhw_+ItbN$mq8o7E_2*s;NlUV-)rV2y3-8^Dfq5qX=T|4vFEbyd=L7}#K(slb z#X#_gwf+`i%=g7_92>kw&tEmB9wPEJ=9@wNf~x|osK?}__nPJV!gS(PNrP)X|Nd;i z_g^j~VgfzjNy;WtxW<9+6gAqsR`9~maKkFFvNeJc$%d1?LnMZEvB+AQ&JiOARaSO> zZEQS4MaVqiL*(ngPG8T7t{2@*qH1MCwV5(!Vs|W`vq`8T@Eoyo>$bWdNo-A%q@Q(1 zEiEmDu!t%*7a>c-v$DwJW`5n_!n+7_8Fga~CJU}>QCPl|&q{}nF}sf?&gGLr*OTR7 zAL2K1q^!Ozoh&xmzwk!4gAfB`yJy#`B70pZbvm$gbrv&X#jOc|O(3jCs%Q}sTyFiD!941@s2GLM=p2`ONWh`le|BvMKj{k1ugo;gWL zWPUQyI;HS^HR&x4%fNrp$5;3myaQnBe#+f44M^ri`~`)D8xryLw@CY!Gho)OX!9US z5R0;Em=+C|ea%U&eG7y&pfh>@}7~KgUaTohfFb{AY~cnm2(oe>z8&bLQp?gDvAo4@#99=>a9fEtwBe z$bWDV#?y>lqVb%*xCIX5@%t>{Hn|&I6qk2~tdW{O6_ixU(K{?o#C(qy70^RVkca_C zZ9~HhC%#N-2+-pM@Y=+3!4M+3f)lcb_EmfDp6lJ!o{k5Cr>MQ*$$T z5T2lV$iK zTtL9B3PR>wKI4Q)hn$@J74N_UMu9XeU2n81tAB^)P%c>TXjUe=dZ6`--(}m*yA`RI z1uP>8=r5?9iK*ndD*)vbaEeHPF7$O0O2$%bJ7Ntt3fcQF^f03rwB+6T>wSbRej9^~ z@60%PKoF~|q%^2o6t)jOC#$xlWe6Cu4dZ#ub5D(53nh$7{e0o8A`kBOA-V`;yTgZI zvYSP3-_CCAy}NNLDypOy+#+v1fZ;Q^qHYkV6q*%LE~7rj#Iskc8%jsB(56iAmcG0G zFML9}h#F~E%!+`QBO+ZL#l>6SiozVo%vaN;a$u?j^y-fYN*C=H{#IjMq75U6WRaMnFxa;vbIPOqP zIOhQ45f*kS12HojOmy`EUI^9j5+oMHn+8_$(Z6%;nU4jwfPMkbnOKMn^Jt+5eum zxuc|nVe{%XpD>yij8XOiiI4sy1%+xfelHlz1x1 zA1d)G9GNMcnM9I4hZRoG9lLCczpWRUVkr@q>OE7}>KOW&TK$I8Z`0``QoP$E7;z#@R z__?-rtp^eyUsZm?8v*SVLw5M?0;VIn=QVuizM@lKN8^rzTxt2+vEKfu=y*#h$hoEW z;qA}ULC@2>7ixtAf=kY0JOec#%zDE8#txT4nQw7$aIzs#E##@~h0M!kNKJb=xoFIx%k!5U2e0@e3 zTV&?{XT8S&oCmO8goBxdO+n`pnT06Vmi2E{@AqArHtWflm_*rUaS=HC|6C+-A8Bb$ zjTK-yXP(>r?hEE970be`qL{{^zjO$xZ})Z}5?_WBUjq;wBHPiPF!VNgbbQX=Sn#L1 z7i22L;U);<^rB^<2&yaCjK#yVAarxTo28w&tUl(~LFrNz7bsX`BgvHbM;O`^^0|JCCsNX}1#HL#S3WS5-7D$Y!|w&kCA ze~BLVXZdObFYUbMF%S&APwA}`hxTW?-ZO`oH`_)Y3(x3u(XjKI;u0Gj;_Cwybee^8 zC-RdL5^RsMxwEkV0~3=QDDW*7x4|eLN#olK>$3o~o+hsjX2))kx~nd=sZ;IuOhPU( z6OgH|?C96*b{kxUj8!H-AleAZ(Eie*u9edMt2Mr~VLd{OXVe5#@*k=>0dSjj{eavi z?^DLv@_cq^X(M%VgAh|iC|ga2eB9ipzb=9@C?xRbtRBhqjF7P*3sktzVtZzc7wy4K zy_B%{2j+6c6#X*xIp}O`k)GnP#lX*bg*_vVIQGQflD;Iwu_>KDma@bMVjB{FPj}cvKM_@9DV{D>>8loxJD> zrU&Hz^Mb%B)Kpb3|J^@;A=DAteB`!9$O~0^{0TJ-T;<*doLEvAD8aWhy-8h<^Ei}St z8MuGnxzK7#?hsSae<9F8-3lTBBVnkclO~zSpw)IoD-4<5+(-RJQ2&`9VN9Pv#^^<( zvk~gFyu zBzynmuae)$>&r+|U7?YQmtPhn6Vuyn3n3M!%(cR2?dZ8(?08i4jlxfr20K@Fc*JhE z0|~UdL4#I3iH!H1iI@Vgcb7FiJ*{*f{0QIY_*JxoY&BD+;a^!Pi7UX6<|{G7!E*poF?=SBh4R@o1eMK`{zr` zU(5s&FGkIdsggA013#BHY)>mfyFJFM`z|Xg7u#Kynjb~pomU*U+IkxUpT>7@fn{3J zLD|_Ap4jmdG9s!lW#t5Z@zYx3=<)r(#jRG*6$7xw)N{tr!7uQz#nFu(XM>0qCeXoo zo8LG)kL_RGK5Bu`Ze~Tlw7&k-09XrEOcb^F3Z=|gnbjf{^t52k8|wdT?>HhT8>%O+`nf5-uxr#~1r^lZ1>-V1C7R256G>Ot zEJA0SBd)-2TwsK}EVfqZa=&J6{HZ z1@r;~Sb79F=LBFzjI&v_N!X>E_+9gP@eXXnqOa@$;0_iyFSWwRv`TyCfyphX_&fxc*l(BA)W3VyFjY zfP;g>KUSzZ;nqXxh3@yu#uc7V4rw%eU2B0b;(>q$`<2Jn5OqrMcNGq}w7&k3yi^wT zSHy~OvHz+=_v4W^3Xlrns35;I5WoSEaFVN=L>A0z8Oy&j4a6BE0LbLoj;IsjM}fAgH6ANm>Wpz zBn7%g>_Zs(KOyT0L`?mgqa~U<@U&e>?W4zh_}jnphh}!5PkQ3|2n60;Pf#!`zxg#- zrB6Ttp25!>RWJ=AJyxW03p?(I6^chRTu&^x2Hk03|@Yk?WX8}_{Ku}Q73#1u_L(syE z`!BBY6psJ?$7xJ=(=YO8%y*>Gb7{>vJ(_i2WPM=D13h+r!scQDox~oQ@pqZW0U7sy z;pem3YC1ZI;`h=8mayc~scrqT&dyb&bsa#VsL>2V7tSuZ8unMwyc$AF#sgbUlzU;n zP$;!+3m!ji$xIYq9U_q@E-2h%dtie#Zu5}(#YxROX?ZWbj*pNbQS);90_HCv{(%PtSYn)U&^Y8(JN-*hGz4A^pZM*H zXP)As0DPth#@Cmga;?pQJ?MQmK$5*TftBaBJpbWx?{ML-%9uCtL8S2i42SLxj1T|Wynwizs*(IUJj+_Z{b(~)vKB8D_zo3$ zu(b0;g`&*e?DW)PTVF*gMN}$9`A!=a-9M%N0xUS%sJf+MCp>6DB~|{w?{9jRF9o;EmpnCm}WrX{E5m$@*oh`MHqL=kBzT zo$p;@FG2jk)@duKmZ(WRSHC$*2fA(e?!_2f?~vA0#n3;W4ld*8_nP}WOBCT*w(V8U zz)5@Pr?Ge9`8@PF{-y5%2MCW@%%yUtw=8XEW-4O{t7EKAqeBO~TCwNX0?-tk8cyEn z8i|FCpZtYRCaKCRwIqudBmAV`rV8jSkk6wmPqFQLDDNc$!-PqrM09#vfWjc&+v=s* zt)!iTThte2N;8ca-z1nLcQVx9T&F+(@Zb1;Wz_mcNX5WZ%pv6#W1PJQllzvTCasX# z73LF)7QWU?+=r0%L6#Q43fc)F!X>|`Nc;qtj$-7C2&F+b<80VmGEOMyh$ue&(5!66 zcPe6HZO=aNw~3(+ky9;=!qMy&&(c?kJ-Sy_>*Xg#ST(Bm9t^1-+x>saLPS1(G>vC2 z!lZtH2`Xx-1z8!qOi`{Df$b+ZW%npcWEXw^{?7?;1p}odcwB`7H`ERCb_Kz?t8vpS zx}T50KqXo0&#&HZLb^ZVq6`oqRpeKaN$|PSzM}C^4iS>uCd8uP@%tI`k&{Y_GV1?% z0c2Pd@6RJS!{4m4Txk^kcH}0|aU^XNA%jw93Sp!+0}js4%NXlX$*|9Ky6`H=*|5-$ z)(9OwHUd7YtEW#$J2_6ZF2fkq%{?umHP&iu5f+gozpWjy9-}C$?%Lf8bK~7E$tTuIa(h0 zYlQJ`<3=rtk@v?Y2{A_xyCn3aV1E(h!5yb+yMYAZ7IyUGO^7GkS3%>tEujdtEEW|E z07PRwd1=zY8a^xW0K8lPvj+Kre%h6E3~=URXTPll(a*1em{Ja)-Qq+F@Qd1~JTq?Q z4Ocrkn0L-Nd(%&k>9b}TdH;e)twZArxxH}XVA#*<(1sh<@w;HrVAJS@=NcSCw5jC6 z+tRb?+6KoYMj$D)!6n9oUb@0sr>#HxOZw%@mHM7(VgS^H{{L7Y3*9ge;eu9k z7Fw0;T5@u|f>qigkl<^|i*S45j7vIr+;N*8 zwCeXE^2{Jns2z08z@d4h5vh^hCLF6g^zv-@y;EFl>?IoVdyr6Tm(QvS02Sv<-b)vR z$Znb`Sx=-d_S5BEy4Zd+u5pG{zHi%$6X{qA>v-fmt+7s>uwYvJ5Y6iM5bU_8S-w){ zd!5pzzN*q4U+*R$Bvfu+-o%((2JmF6=L6d8qJ!5Ebv6^pRh%%D$E}AiQrz;tCog6b z{I*j9DQ|+_??b|E5DkrQv%jV0OS`(AkeO7 zFLz8TKsvO@6RHf0Z(4ek=3NZ=QakX-h(e|5H(KKsMraNbK|;I$?{_mzhA_(gFMw8VT+4t3v8 zxyyGrXYz=KYbp3veFKDFl<<(Kz;5oyG~)6U9xI@>}(<62LrTw zhvp#7Kl@T_zSgqK0*0}H+TBu$|HCV1a+yMT4c!GBCVC1Ygzm;|CYaq=IP*!>hqs}{ z!(2yxjEK5MZW}iwknhl<1Zxj&(Sbm0#p2WKA~_5fKMHbkS7i5(L)d4ZS4sN#a+7YO zBFFsx{w97c7@4^)WtZ{RR{9m!0^P6qwT%z&_9oPykS#k`+S`-d437z40RaIscD}GW zUnS5dk;3bfU$NJP2Nv9Ft70soiYAOjn0f7PWOTHwLT2i1#UUfTagWS{&jS<99NSo9 zBEesy?I*K3UoRmUnR${d#MjrJEVtE6S@*ZWm{A+1mC3SL-Og`2T z(U+GjyYoW9lg`%971g|4v5z5Njy$-XbKs~RBp?Ra}Q?Z;eF~E7=Ei z=6D#vnR@tKR;D6gl9`lW$=SaFBVIX%nuhnn#8|x-jkqw{B(VbScK0^C;pyJCHZ=~ zMfUO~=3m+A4PXInD-W!|v`dhIHsL^P_IbyK)>SZRYh$3}RAAn%1zG6*n_ZOear8}O zcq-Y+`IzvG>W~=>Fh3VTbF|*izP)!S%NHm{l?MGdl6&G?QrrDJ5tH8c>^B`4%x}~y zOWhh`#@T*eBw$vYjwLuAbISYy2J?n%b@=ta)mP5~5(C@X`38y_Y>iC07kjknJ7S@nM235~II)`cmqnRvIqv}FcML_M7IRJVcGu zHqM{K>A%RDo11gNU)XN8kkEsQ%faYH$F39s-}(@}e-YGSe@{y-e4JFQS60Vb= zI4#XPBDBgG_EBs=Tbtf?Be&iH9&hFeDprHE3oBC4?sPG9SO`MmuQIha5|_;Uj73iH zL|Xk`<9C8TAASW;W8#OY2ybYhsUvZe z4OOSfP7$1x*M!)E)Z@fda*a=U@E2l?vyg4>`Zgunx@?ANPaiZF=zCI=oFhQvi+yW8 z!x5^q0Qo%tc(hgLMDC4blwE~>h~60b=+=;} z{;DegPcaUr@H%n9Iamw4$QM*;4UL2due_9+_-e0j?BNN zXm*CopE}jt*@6w=>PsQ*y%8GzMiB4yP@kTj-sHNJHHX~2;oeRG*K3;;KHIsoEnobv zjw~!{dNu3=@uh@zc+A6je$GBu;tx$s8AyyksI3dE&zx0}(&E`^E^>d0lQmg{sWmXa%;o-LEXp zku&GzHEjUEyuCZfx<_CV53jckCyNdm3icsn9zNVh*ZHbo)^P}7xzA?(D5x4>!-{wr zplLR5np6H7tkXR+)V>keL&3~D2b1ty!~`a;2WKvRs?EaMEC(2ASV^CgZAiT(rIPbq z4AzfE%>1IlWO2i`?GN~1qu8U$I^Uue!&`9ebNvo!dLb<@9l7B1=g(x_=`iuj*E@z$ zAQ?bFMNf~0E7q<5R^hb<$i`fZV=}z?l`RTA8%Qx_TrtN$RNR!*TH&@BUs>kpd%@J0~e9sZWWY< zAc7M-o^1DOa}8vM@9jHvBmJ^)jo3^iP|_08T~kWgjfE1X-=OB#cHd;qr8}vnwIP zt&U{#US;MC@~F~fxqjlLK`!70Se!~MfCl%`SCMrGBOVA|SgFHBJggkXH}Lw^Bo`y9 zG>F*JcrP#Y#0@H|yBNiUx}%jGk*&%+aK*1W_~d&(2OT+zyfkL1Vo&@+vkz|43>z`= zMBd99Q0w?mEvGq5JPOu*f`H?z{Ht^YZN!IMU#)rpP7Qj}SAxz1o%tm`ECn_z0@zIV zG-e8^zQ4>-V25s597_VvwKDb6*d$_$4%PcnN=v@3=X41XC#A9db|pg^o%YdAupjn` zA`m?_qDB+d6(Ryzk!f!!g6sdBX$E6QndAqjILmyP`G53eG3QCZ4`k*Pr!ezI~}e{KSVV0wHP6(R#cldeto!m6opu$UXyj$NQF^ zyq;`==zKdI*%9OHv$^OgeREe=y{25VKbvY){JgG@bH#j_zCKrZjoSV5=g$j3HPt^} z&&0o9xZ1cAZEmwjVq0O0t@rpO<9+$MNe`rCDLy><+hwGVp4;eZp(eg_p0Gi)@Pc0!7 zWH^V4Ksi3Y@*9>?b#eKE)vYls?$4$#5Mg6lNekkhA?PED0%(Y1ycga{(6yQ#YoMj` zAEo<#K+{?Xacro?$eWIKKJ#?_QdMR8cep zAqG)s_xpUtknnFHnW)L=f|YZ`Q6pS)?JUHh$@W*lBqB+MK~0#=r=C8K+46UwQAk$1X#^Ciyh;~gziKmX z64clL(A9#4bZB&tR%UXfzh#AXK!5_Ec;l!>RDl>@^CA3j^Xqi3h9+M)YxiZ+`G}un zkii?Y#E+bRb6UDZx?DkWUM|3W%F31u!-wUR z9?+KOKrNl#x-AiARR7WECQSvT1Nb!11p439GrHikm(aZTdN5SG?u4;8eF}|h?YAIF z8ak_C@FL)iTn!th`e=RDx4~7aQnOID(QN(SiU6G`nm?7Vhh8&v<`@w`A>_(|R%gqq zm5%#$URjV+urCgOx_ky3??$(0SmbIy+TB6mq` z5vbA}ByBFNYZ;%0Iv#Mw$~AKzhHzm~>TRYP9h>t<1YQlJKV=1JX{Hc;{novVx*Uajr@ z>zRqL0Ll9?soL~Q49zoqUD#dzlu#ow5Jh>g93~%JIG5T{PDaSviS-#V0w?i|0L=k*2v73x3boeUJ3utDHdSApU%1aw#nCe8#25gL4Byz^PZo9A1z53FIUCYushaHlvMf!s9L(O?ENqCZu#40 z@pFn-@Rn?s4=-vrj@nSFr}~CbbLQLwN_TzN_Qr6nA-pP8U5=5obb#z8b(yHbGfbS6 zqCo)M5Pe_sCDanSG^>-HAXYtR0EyoJB`>4qZDCfuIUbyB=C-!SUKx6yphn8J6YkOh z%dmI23_@mU-`$6|i8CJCc(bg3pxb1m+-EzBv(lSrih`FTbxHH`mzde^p)1n~~ zCLWpMF1mCnM%vjOQ?&4^w4qeK*!NI>5}!w{q)+uH%>DoRZ{H$rx+0=AnS~%#I%uG+e!9felV9aOsw@uKk z`%X{H&`aQ4JOdDGL1BvLY z)+A_q9yusS%yad>X*mMv`}CTc8Z7FM0SIdN>FKEpXqmEXKA1k-zoZ25@KuOqf>dxB zImnDxoX!`5P+OZ&?XiOowPI1W189I^4(`zGmk&s(DL2WEZ@@&k-@QFizybz75v@F` zBOd>j#~@)nz)mpleyy@$cR7$!MV08-jKJSBL?KmfxBN^#kX@_ z-0NY`s&d1fQ)rRJ0piSLQ^s*c^I>AZ+uOSZm@DVdBp3<0xqI1fM=bvERki)kqpAb@ z8i$u%62FAAR z8Y}Vmjo33r0h6w$ce5I8ADNjTjUjD*y3K`o*GbkIv-@MUV| z{H&HkMs*|mT+ zAW+UcvH3ypp!oLjn&tzYc>VdXh5XvYu{S-X*-hsIqc?xu_wyE=boKU~M!%V^9L3>cq9m9!u5#FOa5 z;!BPK;H?kC{ZtQfl61Oi`Mzy;1X<|GT>7R=?kgSk)*jP*QVl$cRk6!Eo?|H5`WNkT zH+!6b@E&-E`kl%Yl$1>-gN2X>9pVEk^&YFa;2w@_P>-+M%AC6tU|FT5D0C&+g?TK;5U;#qKL&u19iP3X~Q=r7Tk0a5GX z{Nv7bBoIi3;iW^Muc=0m#InDt#{W3H35EmE{rSD*RohyAaw3ro~!Lo0te|Ft+ z{=54cHKdSU=p5GbC$jk-2Nhn#0#2sF-}qb(70~2ZWX-Dinu#2CSZ@}7rzXZ-)RY&A zL=wyTM8%yppL9f*Ek#-JsMI^h?MHK*fh$4E4Sv(ejPbH=(9=-!ekZhYpH`~7g^m8; z>(~Ln(A#)EQpkewBkkv9q2%}rAuj#Ebl2C@U);Y6pk7m8!soFCj-Pr4|85U^XO4OW zgR)HCYmqrEt_YC9KZM1Ca^~^5D>u0-g~hqa<_{B-=kBzimj3h4C4lMFSgf5z*<=NF zQ`Ea+g?1RkUl{RF2+;b}oZ4eX^{2oWGU4NCa8714C0Fdkc43gW@*AQ1ldc4-XO6=V zA;vc{doC~gV{)!)a9QJVv}`syWehg8Z^653AA{y^Bd^gm*4+yf5#DNuJ(w2 z`sAaaIudAS@EfHcGN)ENT_gj$Ln4P&6?*!4P=~58q0)C6g^it^!N&IX zdzl#zn-?eXt=Q*Z9AFM)1o{X{t30VyUhuDvYZC%4@P)JrI3<$ z)q(b!gp3-%3Nz}PS(^k<2se%#rj}RwK2mqWT>Aa^%%1C^f6w8Xzex|3z1QKLsdSdt z&$N_i-hWA=Q(oN>c_=lE+|P+^J*PrRA1rxs9~uA<2=(6xCHy#;Slz1hVxzahL^q5e zbp^8pCVX+s&c8V4Z$;VsrshpQY!E-_x0BcA&*B!aE#qfs=c>8Dm56FzxTX8_gsEg) z!pS*wLD5C%j(>~k{A=sf%z^sOCBhf|7Q`aAlEQz@H+^ zQEbYZO^ncvAKb!@(6c@S>=q9+A)=M_4BMNbDrzoGf$cVVD6a8&&F`H~291(InGTfg zHu|()Ln%MfW5taw(t7i42akY{3yn&nWtGoqk)Ig1hkT_h&oha{+)IbK`Fm4ZgT4?8(4# zO0cm-W(B7hbZ~TR&@ImI_N_j0$uN|3UA+M<(4e$Z{yA5+S7ysBtZOo%6BnI8=g9Yn zIm~N)DF5$N$k2Ny$r%*i`ewHq5;m>87`PzdgH+1~fE~|v1l3E)4$a%C^7anMFIBpF zdU3I-ec-H=Eu(aG-#0-wv{gRp#`Al@yY6ND4%1`G@Ha#u#h$#N9GMf;69F2;u{#{l zC-W#)P_ZsePH(mJw?+b5by_XDviUpY(@Vip@gU>j%IO`@*XJJr`itqcmI6GL%0B}y zGkf(T5&S;}iD$rATjqOH|Bwz!#Jm0ap!JMu<#zA*JnUY8)Pa_c7Ukh@1Y6VcN*poR z9yzGO4tC}-nbL7m2yzZoE!`Ndv0-on;cmQxSCh9f+vA5V&2v`A?eK!6PZOXTM zpZFCXj#Xrvx_4u?4kls6?jOas?i8OW~Pw(_L*m`vz4Ul$D^vAjP z+VSiQiBP+pqJTh2OUb=uF0M&R`@%~sAi@G_ksBvnhYT~3n zSXC(O`7+snu_y9I4`TvFnnnUxXkTHZ9j{Z$%=6C&cQUZ#mK4K7;}B0d2(sDQYOwP);RdoGgklRcfW7Pe+0(wz+n8b zyF~-Myfq=(*Y>nXbLFbAxEQGOJ46r7j>V|}IsHSEM|rpKHh z-~pWx#M;yPwUuCF=)lXsQTqvh)ITF^aL@O~0C1V}i3>MoXh8QThH>Rb!}zU+HZ-fI z61QV@I-eRu$Z8n4k>$%O5c8p6-g4>U=u7XSz@uWc1)Dw$w6He{{?L|ziLw+q444{x zCM}(}l82E(rV9t9UaKRVVn_c#a6C)9eVHp>36e)spEU-lNBdi|l{TgkK0P&br>MYQ z`!l~%fQ8@_DqNBYKnC6LXmRW^=5gU;VXTx(lFLaSIY}j_9O}WALcgEyYwRWVSAQ3Z z1yN~(up-zZ8uU=Ji>i)%$1H}3()!FwK&vs5D*$)mgYqBj77}5{_gn~GjFf3m7%#0p z-J^s^Iu3re7k~1N@^AfUGf;`m4W0-~@cp+jHm}58ua@``hybjd!Osw2B#5CzI3D`n zWbW&jNGYHKw0j#|i3O6V0XJGW*W+=mcz zJKvg`APJ{_?a3;2Xxdu*5^8f%z=!BWnP?bjiZl94{s>0b#sG290fP%t2m9e29G-Wl zW&$hJQ~mNDDvP4FHZ}~C(O%|AMGN9L3m2@u#?SJmF__TyC&H5r8}~RKyd**M6*OeH z_t&0y^my!A{RbNh1{>)$8B$PkAfjS)B*`E`1;*^WQt+kU@4p1BJVs87N>o7?Vbg|!jQV|96>7N~GT7^ZZH1OO%>wycYQYDQYHU4ZN&%l|g}$}*)-=9!9|gSowhstHBwi`pfX6M2;ZCju`ZtkX=>JY{t46|g$M zZblYM;*8T^dCwiN_G|K(!aX!vBGe5~IXGRyqvz>RdB=9}$f)Q5LTXZFkxfiwN)EUT z=bndW3hZ7-lqts&F<27Sj^{t|ZS<~xT;%mTO%C#ps)8Q_!UP?HI1EtK|NIkIPos=N zB~=C#nB|6dgT*B-f6hPR_oN();1@@uLeQ+6tXNtYMIcs=xvAx^R@NzMS?`s%8k+bb zC2Be5*w)7Gz2m3jBB9HUap1Sv=NR5?32Sd!o$Wt2RrKUH=h$;ZY}@;fH>mvZFKEyr zQ%Ox0^mZv{Dj;^#`n-hiNl}7PHX*ec;XRp{b@TvvV9CrS4uJ$RwzZA+oiN0e0h9r? z3aSv}{L^RlP?L?i-6QYVMnzFs3S0>}`e9`&-&SPlXbSOoDu~Phj)$U3_=~Nm$FoBd zW;kRjvkDXJ1C@gKY$V7}X*C0NIZQI95i$s+c^RDE`jMpmKMe#di9Qor>iK1DX-+xL zeS38X`6x0rk7R%`oi}=~)$`+$q$)C1V3R6!!2_IF{OeIhSy-&OF!Zv*YprhQn@X;Q z_gf$fLmo$vMoQ?x2ZtyCu8mS4i1uYL*0Y3gkb8nW;WnG7^2(huFgA!|*J!RzQ5af7 zg~&AA`i;9_N|TNac{J2#w0ETAO3@W^|*;A$bF|RiK;p9IR~^b6Vg?ktjm} z>&RSW>1O{ZAGcAUl|&BN>>HSH6oXHiqV|u+^KM%Usa~o;tnaffKqXD91hypLvQT|j z1CW+^AI_Nc&SEU%8tvY>k;WtBN(a%1z6oNdVp{2&_JL8N()b}Dlp&0D-i++1)l&tA z7XU8dp7h>m1Yr03QrI=SZgjr|#5IeoHH!UjCI3mBK31 zLCfHVpGGZ^SLm)jlom)Dmh9-!}*bvOAnS|22|&{R&QLIXdM270k7IqR$^hK_?R`wEvO;-L za|IpxK*K-djc^#RQx{=+_Mvq!XFVIP2<2ldZY7>M`YoTbeth~;ASt3B>C521K!OIE z=U61m0)cPaB0-%6D83RaDf+Ha80vf(pm2MW+s72srcM&nXhx7($w=xopMfakG7t{J zGCo-V)-bb9Sd#hlqCH1q8CE+n-$q>c5VB(mXQQT> z=j`$?)28u58hM@{6_ea#oC00vtp(!nBZFDk|0MfL5z%otHWJ}pV#YLE zde_cwJP5G{^ty0^wTec7cnOKQ_4viHzmOT#=KO6Bx;EkcekgHoZ3qJZpX=u#LPiiL z-&yXHulE%h`HbdY%zN)cyr_YqWYvmI2ZkEWeZC4@24@LUiRWL$C{a;aV}r~kw`#HA z6y&VFU<6j=GpJ@MjJ(7i!_vc4zr4ZNC(b-P4i6>JiUj4H{Kgjpt;!vBPs( z9T;*74B3&laMx8@tM(_cYNBRT_Rrm@LF;T3+@4!j%d;G=}dhX?2N>cx4vTKopTwH(elEA!uDpjG}) z8?=bmKoBhHnqazC=9O_K&V=K)!dL=fJ*+a18Cc3bNMfL6EDed^`16(i-*Rn-(I2!wrXDM(OGtG-rxrh90gNMzRQ@fW@m>f< z$!8-YL+*}1eTs5D%y*(j9xa?4Gj344?S#x(ZJvDRJnnlQia}#c`~KuIo!C?|Cl~Tt zDe{jL8dt^QqQiSTrsF6(ct~1JHdNq+A4uCCKkTFT_+6Rfgtrfcn@N6n;hew^kBop` zb_j~BUzXhH<>#1+dv=5^D6m^KcF-T6qf1Ej-Q*zZbKS9|fm}J|*SGE1CwET}R+P+V zi-i7-2$}9$-~3!Z+SD9l#0RC<=b3$eN1o4snGts03?-GkW#x~$YthDvQ)VWDTwLWG zb;o9l#>J(rt2<3df>0msbUy+sV3iqAuO$uDZ>P#|iHWQ##G^>=-vwaxn+wuAPbbgE zwK6gaB<96!SnxKuY!1NAqR9lQL3<(qdM+))%_;VEGW0e5Gq!Ep8sjW}vWH=HkL$|) zMr?@!4Ff*^LD5AN+k$KSZ8MU+WNO&8f{f^MQ5Atm(4F_0?L!+WQ&Qdlnd$ z&$i3Y@B9DZjYu#L?w(tB>N|hD zcE$X?UhTPa2Je?wymI3%GDYBo;w>0W&U`{lc@M*SF_Nf?k|Yz#mjTb`g)c)Ca>dbE1SwuKl#3kR{r z+KNf=CmnrvI?f1sf%l9XfR%hI`CC_{`{}I zX|u-8*XIS7H(G5Ea-5n0F9H{_7jVk>mAa=rs;R2{WWKym9Y1Z`>lF(FqMG5!Czp>Z z9rTcGJjGz^wO}DLEghpQCCbWCx$&su0Qh3@$lknZ+*kjq?Q+5~^co)o|GMW={`68b zZ7s`5*$-p)T4pjIC!ok4JQW!qBHh+M;e{;?cyW}P;l%Q*YOAP&6Dz<_Cy5$nmG($z zUhu^9#;POm#D~d8OiB`jvAd_Y;j*H&k?fED<^;jzYXej%_UazyzatqZDs#YFpjyE0 zXwdz{DSUWBc)A{5>|~9lD>5nm-`X?NVYw2Kr!B7CDr(Z$9NV3AT&PF;Y{)QOFTlPU z-3TuOUPZX*oe`$={Xmj({CKE9b+b$EoCj^|lD9p-ZjRI`Z$ui5D}b zSoPX=IHVtwyWXZNxgkSKo+ZA*u$7F#IH`O3&dHnhLyFSRc;+%qhLz193Bui91CI{w_RMJYP< z?bQqp>F3uJdijDrbBqOZM}4GQF}X(0SnzCTji%NM-=a=n3VMj2BVoy(8{i*H^N&6H zoO`8A805#(@t)~!*42Ffar>7Iis_wvCU)(WAugXJ-*@kX&vzmrQnOeC7xl!N715eU zon8OXoZ)+LKkDH6-|DRE$uu0~_e#He+R#Czp8rIKXZFpvt;yi|{A-;^FQLqt1*c4( z!7@0$qViPDc7}N-nDp~ z4ub6Pth+CN=JlG(Ie8&L%SSAfVKx|`7qK=Di5~T0MyHH7@LDb{^Cn2YkS*Wc6;~pU z2%S9W$074~3OKN?{2AV|O7c=44(d~96}RZQId^$-AzjGCRQ@dDM#Cd=HT`Q-3$6Q{ z^gSND^1^i-`35`k$YYSy`>9=>A0=4Aw2%v)LM-nKJB3bl!_@bO-$?T}eVS3wMw^)% zovb+usMuRfiMPF#(rK463f2Z*y0w{QubX|G-W-r%jGbR-h~IyL-})EWTxcOTyte|Y zS^f{cPAexbSqsBH3lNZI8cx)(_Gm(*>(bz0D4wze6xFBR!f$Kx{LHFi|D^>pV{*{a zyx2!+3&&?kqiTPf)l%U0{#}yXg18_#_+v^Qu1(KY5L_S-VuWdCim?vPY4&_mK z{u!42&t?Cu)?dgAt4!^2h8N)f$?+Ha14V z@2$bEEa{}%?_<~3*XhbP%{z{{fOU$~Hl7#AgSyPg#LR#m;kq(=cH?;(F*vr&+RKR_ z?`is(lNOq;Fmzgs!ME}Gv9+e_{g-Dpv@f6-?P2))MJoHajm`RZ+q)MTY-9`HR*e4U zUDc6x-c5LT1H1yYRWt27J`c-+VL8t)tw#Gu}uZ2*j{_AS1DRR&EhyIlBn%y7a zBSN(wlqU4B5;|D=Per#rEh|3x_Ne#(1_g?rO8hP`JDp1i=oM)tT_nG~DafhtB7nj9 z%@yQqG|lP;@;w>k+%UJ@`tAj>1%Hv2%E0D-wnnRT^O!EoNuw%abQVe7muomFK+pc(5GgM6#a+C&Z~)-j5=8>- z$gJ2d8rELo#=N@?*faFHs?sv<9t%A#xf*55-OJeD9Tf*Ro_|ZFk)!iQUc?NsCka43 zSXU>4TQzHSExz>TpbK`c*7`R_p*s1^aJRtTX5ELc#RW%tC~b#bf!w0-4E9r)lrkgs z-5@O1vktzMqOP!mL1O&_*A_uH(H69M*KX^wP30CcuYC1#*h-aOk#RSiz)*wdH)o$L zBM$@?j=sKvrKF_HB8qv2v29hOa~Nj8dq0(-x%)CezOD%58yDr5WrOE` zI9}g3s+{%G%}2;YeuLZu4$@{CyBJBMWUev~ct_J9Qc<5zc--e01lhSFuXN>lcH1b( ztgZ`dglEGlhCImIs{N(KQzq90M`u3saZ??rYUv9@vZS*3@-9V8v5?!8g6rhRzV(yH zZ{TTOJ%BeGet9xvn9LH9RI#ev;DX8CBrLoAv1W0|5GkrWbK zXHv>hSGZQ@MX<>T$iYyoQEgwMfa1;T-=3knrakXw?Vb0`xsJA^>AZX>j@v<0@FHU!1tWA_H znTWKUYT0kqb2gtC{e-t}{lSxfy-EP|=e-PGF@}7!MxPBh|JcWqs-j#T>+ltHkTgs6 zoTZ~^N>uyngOInJ&+KmWkWL9&tS?cpS=k1!Js2xVpWxqaxzvw%iF{!H{GrwsPf_pL z`w48K0@Oc>4Z=EBiwwgN(c4l#gZy^*v1iFn4E6Yt^iZ||f(g>4(#JO}6Rv?2X4Hih zgdtKPv2S6mhyf)NitoN&%nRFs+&+OCw!%E-9I6aIly^uaa47rN@h9))BMBYLueoPd zbLsHDmL?(`2i;V9?%ppH3D%e0cY_GRa);(IZRaqTw~NxTuWZZxe+Ro8QK zbE`g_fx|jmVJeH&rXz?#r=Bbr@qRuN!%_XFdm0F{|B!(zs2rLk%s+8noVxe>Ap-}{U%rLCBt6LZLpO{Q z;RI?ewVYxC`N)cvSuMzJ4%=)a=v73%yQ6YPL?N?Huha0?5{?B%6fCMj>dj(joC=uS|vUGL0l@rfs$SRD5I_jLXKW`E$tR7z!4U=TdqQpztqh3k3Zg}4I5Gq(%xAidSwvG5o## z)t~@DS*bO!F>h*ko9B_!v|+C}@w^8o?%&wsLL<0_s2n9$UJ&o6Ld4Xdp3*nTi0Xcb zdp%MiaU5LsO0v1*{D#F_8Bk$Uw`uX*S2Tq}p)Xp!ac=(WDN#(89$HoELf1RVxST z1}C|+o!UdUv2gxw9FnG5lnX74jd5(5mjD}QVgAuFm6U`=7wHg6G_5a7xIAQWT`yN+ zKBM}j^0^RNwFWNv^kr6SLJ$C_*3txZ->RDap|cq$aO81-8>`gKw8h@Kr#QR0y`hI< z;A2fDhdg*_jM=2jdFO4VX^Ffq0FM>U`CnXisIPH{+P=T{qphv2nbpvH`0pKS^La*< zx*S?~5>jlV*xgJR+WptpsSZ67KYol9zjHLuic?Pr`Y^(hham9iFO;gqbt8y7^C~JY zDU6Rn?r$Q)U!Sbpk<4?G9JF1P1co>Sb2V%9<-EbVX)h-HMWdYZd!0t;UT{zYd4Roix%4+_0FrpvJAc&tWe6H=o9-Xvq$qa7z%@7U39CztA0TYF}wN=awcL?DoETT6nI| z!1>ZBtcR%lvIhp>u%Q#eyoSprJuR&n(PhW*osvQRWrD#gI*D2a)ZC@1_c(>m#H3ip z%l*eZ_lqI=;_#mcuP1jd&DvR<{-$@(5+^v#HB;Di=g^{1^&L>?H!?xmEop5G+@604> z1tW`Y3k<*?F?4cpvvRtR5%lw&t>P~)PC7`ruqwutU0R(I;%gCp`Rg!TeLLn2jwAJw zfM0l*0g>R>{sUAFTyJM&-cMV3*+}zN-(5Nx-=NLkeUl|N@YeZ}K4OVM%7-;+pfJ?v z0lcT_ejiQ0Z*vs41lEg0@{->1QVL)A8enkhWH8*})Ij+!Rq|D`P6zsIMNNHmO7_*j z&GzDHiAEqY&|1){w<}gCXcl;S_uMvfGC;sOTNPTb@M-<6_MK=fjY(x$KF9Y9A3YqL z*aw}pD=$F|a|Uy@6}us8=F(de!I9PvOBe5$OuPT{*?7rw_@TezKuzDahkr|@`*s*S z4p`G?a1xy1+LnN0%JmgiLYxR;9dvPHV?i!&a1AMY`B(7tP&uNQNC}@TM>WbIf!MQW z)|ZU0Kee%d^__U+XAjbi+Hh~(tH$)=o<(`;y)+!Tfj{}wn#6}hlF5ukCZlGCFFziY zN+iWGg6|i3kENK@r6u+2V6fe&QHa6k_tI=k?Y+ti#dmM|3hl4`2y5)t-!W16G`%fo z7d49Pc1Mq9foZpVx~YN$*1gc!zIrmfJairYC^p^aL7Yg_W9kY!28wct5Ow2@Gk$lFCiA2$&C3Kh;pBqYba zJ{al_ok^?SN@vmifZ*7;mzQ0*JZv_%>BG8gvM+jeLU?Ajdxr@L?(OcTt$)T+a`=zf zv~_aLxq8hjN9>10loVrG{9goMTg8UsXD54#1C?@llKVM-JSF!ekg zBBd1nZgIOuspM@AobX~a^ru)`TOgeGW+RtPK%m5X{($bsv&ab$r7@qrS#`KUTUqqx zWtn&euJ2+|U0NC@DkflR1cSjyr)TRY@b2CjF2|G@It090tn;7-S&`mn*%w3BJrCS) zVtbEvk$tZmd&(H!C^kQh=JnI%WoH|swFSHvN_vMOO0`Ai|L$J_Tn406&p1Q-`Ltm1 znh1PKQ5K=va8Xpu_0mNUhgYMXnj{{ zmz*8BnQdgHLvn}{q`QB=tj+NZI@ybDlI0%r)+^mZ-nxIP{>VCv0~JorAsf}cizy7c z4e1)jQ3;k#4~~mdF?}O{-|^*I^!a{w|7SLt&gU4SOpA+i>3MTBo_kul6^BU%i;fGa ztjr(@`;eF~hpq-1593rB)*%ZA4r9CR)js@>YU{lb3WeOLOETXrUfT|st!leUDoSP{ zdj(kWJ@(*GCk4caek+s5Hq8Mf?<)g}rOS&1c|)f0aBzxu%c_l65qoro`3R53(LjFQ z;Nvv%s=3D|4T_A45=P5X8SIUm$re>eZnw6X)XLK`MmnrB0Bs-cyN1qE?~uqUzUAGZ=H{^&tswOXRnjJed|#Ff2J`s{fpJUr3jF>KAuejlqfvp`Vs2JMKj_Q-Kp&(4U$=uatbKRsV@-kte9l0-!Qbx`n`2_!ABZh z46!P9t@oOk^!az>!#0v$TuJLUd?Zy8bj8N#GsIWgsNBxM-q7l)#j~$;BK7+5cDAcJ zj0!c$aFy`%&--$7S&$=$o-zjz+0;_;#b+#?fu_bg2ePiD(GP3)wn(vY-`)>C6xuE( zN6|dR?pZOJ?$ZG;yk;q(4;==^8D|t+J+B#dPfS&>w$xbl9RCnBSnH_jUG}pJT@6bI ze+v?;Hm|g*5=zn}>dDG7^;*niMXM>K^3853oM2nx@dw!9n5t!O976<-T9@5$Xik#_ z+F1`srPJsW9FuKi-`;hIlLH8CU^0@jAZ{Yg=SY7z^;kn@$ zy`LNr(z_O?_(|Ne6J_k72%2jN+TNz3fT*?ht7ADexGfg39UHf!-B-U|i2&bPhxwCU&T4UMP>;_)Ce5eu~7@ zUh!(e+H>GMob&qU>J)?_bdyg=2wA1Qh-I@5RNat|Pc=%|w`2rqV=v2_A_>1a_Cm zr8n;govX|A zxAHiTg(5!p*Nj{Dg<(FKHv;u6giA6zX~I^ep4|wgExG9QEOfAq{pxw{WOD)Ybx~BQ z5HYtlZytF2I~S>t6Z*&s3g9$M`{`IhZGs$3=zxbN$i^1$7ES@4#9B;}OY4{M-z)v; zpK`2KlxXzgx{uKUjboF@Qa*Z!mk%|HJ^|v zmD4E~e!Baew#{T^5|j4zd-jENR?-^M6L$vC_cBlf-*4D3=!%RlXRr%I@)}1zv~9Z3 zZ3gpOzo5uCyQ5rXZfi#(W18eIYx;=B`)86>I-4L)_FYAdyG&(Mi*i$9aepMdi4bVQ zfL=2JS&w3Hf)3xeYJ?RH8CT!ROvh33=))}4q)XY$;^iJ{qGNoZj>ewP6s5P}o9HA4DgKz{vj z6f`B&&Q4oV!ZR zxsv9Z>z-FCY5!UkOwmqp8=QsT`o)K48L0Pv;fx_a2r_|elIOPTrDz#k0Rd8CzTt=@7HfOF;_ z0p64I0qy9q-}YbAT~bMdhMPs_Rq2hX#YRZ}3(mWFh3|Kn^y-$6jlwRO$i|V2b5m2a z8>I@f3vS)v_`V1$*cbO(l~0)Y$k*z%PJL#pMIU7;99o|KQGCS65YT_RT?GD(v95HQ zt?cH77tkNf+)rdarF+6T)^$2o0aJN0qHO0X&3y?jHaY}Wfa&vTe0=Ab62^yS|_OFRUEMkc`5$mphI}FpqG7mRO^%T+iT}K3P z)ZSLwz|G}r?5Ein+{h!|sto+w10vJfCLsUrsOb!`Pt$?NfooG>e%a)tcUn3JS_J# zXG(cjU1@O1Zt6*CzQ4@Zqh4>Bkl62dp&r!Ch>)6rx#5FXy@m&8jt+MY-%%Ru&vi3> z(_Syb+lvvA4fd>P|Bt_p{k!nVED8EN3KtthUyO6u!{>$jEk_EsvJ;Z^MDlHkLxwnH zA;gNf_AQPFU4e)8Rp=O1Fgs9`K4@4Rt*(KS$f-&c|n$R9<6 zV}Tq~=g+C#?m_ho*YeW`&(9`+?jZx>ZXV659UNMw(BNCWqa`2Qizz(xPkmw!uQL}KJG z0Gb)~xeL5{d2%ZYiWxyUMz_^%U_7E3yh_d@u)A+HpBnNdLG8oQL`i-!ea(8&&#!a~ zm}WFw-`g@|7<40T%7xKG9$7c@L?K`}d#R3o5{TP^$T7Tk(LjdL`wPljs9t3bsO3Zj zRUSfjqQAaBf)N@>Ra7fo;I^9Gv7)Efw;e~sVs9I4P0oBTXwQqtP0?f@sSIuHUv<1K z=L>B@1np%hu)$l02Gp)`r#`g{55(i*iV4n@>v=KrHuK=H*Wa5CE%)Il2C zsSZ$Ar|jHun|D6fVd}ky11(C-hQ&kuoOQmh8YmDDZ?qs&QVK+;f-`Yj6`pvA!x#Xv zB&Q3l_tt{+&%lS#olI-FR@AIr>fkp?->Y~`mRu!}(M8W;*E8}>PPo?`tu0v^$(erV zf0aoOf2)EUsU$D=L&C!2EjN7N6`{nEGA2ohNz1tp#fb|I>Ai z{X!mT39~sUF_jA^(N{vizP%XvqUH1M3wBvGejyW8Ep;eedWdvLHfnL1(h?ZRa0RX} zx3}VDrRW}o#=SAjW=mH{ru*cz3kP`My~(A6Tj%4)+Zh;7SoFG`#5#SGlL{TR_;=sP z;N*SObn-c}grX*nda~on@I3uCc&K`Cfhl^3npESs!-EAoEwgbcV2P{*17` zyeWz=X3r)ID?qE?;PWOhpEBu_n_-I+Zjl+%Ea_#+i=g!B;jXj?H?oq^i)dlcRcEuR8R&E^GCL{@ehb%f=ndStf zu;@3RD$!5+!UGeS-g;1AtO5$v+cIYuz>VTQpp_s4!k2h zftmGa29tq_AMNjUWc{yXsN+<^F-%0<{)50kxC^KbOtPuk`B`gvRW<mr5C@%Jb=j}oC(B)%+2Bzd}-H#u&#;}LoiO>R zs1y>X;lBm}Ffjcb?dqclR>XfSim_Z zsfCeam70i~k2_+g?z~ATkd?B@hm)H5BDkOVYFqJD$C!dUq;^6+oMLmva;1O-(Zia` zN=qFi+$bx?{(X?39h}Me0Sxc4z(r@7eLQ#*c`mp3Yv64P9N@%7=S4OD*Iy9xfu>L| z^RH;uo&Ds(B&B4yj00J69nf1gsh7ACx%ZL%7tg%k(@h?YykH5 zeAN9fh4jBi!DT{&7}D);3VQA^1G?AO(-C?jsVma*=zU;Y zIlBl;ZR6r0MHmJ(d4ng*2BO)?c|1Z#=b>L2?1*+xAqP z(5w|dTE^=$`@(TJap}7uGri<7?x^y3IWZVGoh?UustPq2SWJ1tLA48u-TY4~-LHmN$oq8p!fF?% z{m)73W9H41-+b2Ia{sI};gQBo`fA`G^KX17v1J?+^5guVtvNj!5iQ9Qe8XpFWks}} z(TdwvzODT?=fUXpFEr)K>6t6jy#sr-n4+)6@n`f-54CA=I+RbRA1%a@=_BrS^Whny zY9(j^?P&j66>%P8cYO`BZIr07AM~XoFr-+5XW>%7G^J;66ck>-{=kXdvJ|&Y23Lrn zGV@0I$y1bYZX~Lh%;k4(+YYpOwu)SR(lU4z%JO80#45o5w~uJ--iFkQk zOIG_Iv2~m<_8-mb*=vH8S2ND88udzrbKuA0vvDBk_+xV~8HOy0pMrER7hyoj4suj# z5T?C69cbOY)8h<0&tUx2%jM?0LO!JhZwR)<@(~rPH|k2kmaR! zy&1*0ufU6Xnv#w%{FmXpaHzc_%vm2xTm65(=4EnXqFxt~F1g5SLkZIz4uX|>f1$P;bHBg*8;W82u6~m$v6OR& zpiF}g>GG)sFXY?Qba(5p8CUZ~5TOCbMnp#u3IrUt;4nf=8T>F|W8D^xzUt3pK3f<4 z;jk&PaTf-eTlw{xF>B)|BE{8Xj{=lM61ghbio@pSx;59Yj0T*>p?SyKRi`g+3bukP zuGg@~jfpU{h(A2!ND!8NvCcTl>NjfuM&K2h0$I$s?z-W^{W+38q{+m zJ*znOS8dviIB4J#F?R!az1x&!_5$~XtzzdSydo?-39)>|Usc0_Nam5+>)DOf!T9LU zHlxNrL4F{2!+gwd$6{gU3JoSUXmF$gY~KhBa|1A}$yMO%HykqAocylAPoNO9*3r=c zBVuUPAV!|5~;{Fw0iT5y7M9t?hLHUABxf9;Ks8U*MD35Y_-2GC%(Ff!bPazRc8+=NRY1L+lbzju zJe|c92d*_JLP^Y^3|y5*%yveMlSecnwb+{~ ztNzKT_+<0hrzEL^n=RoW$LwVdZj@5ccR{x`v!!(v(Qq*xuU`nif z7?VkGDU46w!{bU@M`z9ze1ovD;jS={8#MgU(QdLv_**^I;sncG0w1$J`G1%<;rmr5aY^+z>J+GHp0AZ2jqG0vAU{Y4A zj)yCLM`VQuAY9Vqz{?jTz5&~^<928k`I$)V>t*I*5ih#RQVYV8%p%)&Y&b}6G_1{l z^{M7ZYXr`LHsCY0;Tpcr>HS7w8M(OYCSZsM){#F%(6$3ypX1`=ae_EM)Pn#?L!t(P z6Zt>Zn-)p!`wSwjqp+dxr4cymq}r|zejv5TH+RIS@h!vw!fAaqB2y0mFvBK1S`y*m z!QQ%S>+HY{ToW*jh-h<>x`DBpmq<`tz`)bOqDX(%dw$N`ED` zx}2Y%D+-WCTSE6QphfqeL>D^aI*z@dy<+E+ub$p!|?();52GWyUrK^keGur zxNcDKrAhW`S5nK^zFZaEPm)=7@Lw^90}rNyOE5B&=lzy|`6BRg1k;GI{qAW7EsO@E zR|Z~ItHa|`H%t1mf7x3#88`Oh%rcUPP=7}mopjR#J9PfUcQk!w(Bwe{=W~No$A1c6 z*RknRFNH)6{{~W4*VSzdbA1$Ys{^52akXycK}2JDia`KRVq^e7jw*{48fU#qRkCrZ z6v1-^%CF1@B0Dk)I_%Vqhu<2@YZ+I3@9V>)X>`Nk&j&Q>YMa+uy_uuEnTE+b^@mmx zn$xx*PcP1aj|*~mqQ}h#H8&fWz0ysTaQAb9pLK4(*Syw!Jmf_jP7IgOHN}>(3+pAB zTz8l1JK^AoX>js*GoInXn{P)P9gEp!3Iqg$2yMz z{pBBZrU$%Uok0FA-ulJELa-_6i7(+Y!bZ1j*_oKE%_5so1%#P6`E7b^1zqBFMZ*4s zC$hWI;XFseuO>^m3!McB4?x0SvOh@iz8QHD67;SXrxY;6Mtw}<3f9XIL%DiHR$S`I z@yS9X8DV^=-$p&AR-LNhJq6tw)N*9-s`vY)dGfOC4v2>N7$*AqBG&p6g0}8S3Lw;( z7?U%GhVoT-13cE}{ySV`GlV0sQB!F^*F(&q(tm2>_lp77y=WMQ)H^r%6lo=;VSC7j zULe|2^XH51&2NWNA2Ha{arnGvr^s!lL$AKyG&9Sy~6&adk1`9xx9r8TL|HMci9seJ$2Bn^kxUYhxE6VB;U5=>7Y2DB-|wkBXJU3rZZ2&H&c#BJTzNB%kR%8K`U!$G>co(Lp4@ zGj|RGGw8vHu<>}{L2_H*10+F&;b9}zxYU9`J@%;vLZV;=nv*-6eC`{V9$+9|hGKLf zGlD^3AiVu9{&CCW2LMScHeXe&R`_Af8fC>jiDP`qemor+(JSF$D zrHxUf{+64-0x-L#4gQoLTJzyxUIWxRXzC`}rT#LEYY7Z%9+ruQJfItr1 zZkJE(U;LKeO8bA>TQS1qVyBC>blC_7pJ3Nn+uAt!M)FwcBJ=3I zH|yYAq1^=1ER$u&L5?vKT(URR4^*INY0A!~q7}fR$w8p3&<0=GSE%I~{L@bs-bj;m z*+%1+*;kBIVXsAu+Ov@q#;Un7|J0#6k+v;@3cOVPNg1cDoRBVcYHI2ea$6ZpTe>%% z5zvxe&0uETE;g5|DHu!e5T~n1N7o`nPZeR><33yGg@h?Q+HOZ@KYP#p= zFvdWA_+$#y<^+KiAl9%pPhnbYk1|Tc2Na7LIh#abmON4$|j56pS7#4QyNfkaNtM2L<t z&IS1g{`q)@*^Ae9PJt5Hy+o!jz{1gP{!1wOj7bLUP+4B!Em#-XM551TjaQ;Bdcc^BHMnooa%0j z{pWjNMjF$Pa}|*{5Wjpk>Ynjt&da;mP>8AzW}KnLi#{AIz5AFw5b`uQK(7*GZ7l+ zkQ~g^141^FIu^Pgx1(LerpqAN_JFc@>k+_zS9Ev3xBy|o=-3BlX<8*)?y?8Wes}@7 zsT>F{!*q>iBXdUyvQBnBA;?C}mHA@G@NGSh$Skz4gE6qih5ZY65f3)t)1E_3bH*>_KXgOt&` zo^2|MlY=1U0){2>a`yP3gR!)%Ir*S~T6-f38u$`5rfP0{flUsev0Cu89!QTscN=}V z)Pwy#vO)z6L=2Hak{F4`fynr#g^#ZyEB>uN0#FEkHhjbaEqHLn*b~UNSfylxQa)NF zIVQ7HM<#QK7m!{FVW0XAK7iqkK^ZUTpxbsE8ygYyl}2bn3W)~Q)&fs~ow(;_27|IS z-Ud-_4c%kTX)Rxm)RzjgJuZ=LH(j2ntrp~8$3FF*IpB1!e; z@B&`i>92J_!$oHP9YtLnyZbDXmcuT%mJ0U(>A*@fF{+<4VLSFb*mZ;=Qi}RpK9xw1 zIEo((jOT69)tvsOW-qpXN{ILmFBmA6Rc)e%s-l!%?a1>LWgSU9`$x_JuxL3OUC5Eu zCb-N@L6pQF|AJ)?VE?!OrYM9-QG0OrIKYe53OXA-mvxXvrYr7Qs6Rk_M ztAyCe)*7^l!@*kQlFuS(L+L!u=luTuect=~exB$1e4poQ`z>%NPu-AzS_w%)z09R^ z^DMm&D{58JoW-ZWVE=JxxFPLU1W~cgQSO0I7+zLk*e+Ec7`x9o|GJLzufpt_^q@f- z$Ug7zxj26K)z$dAxdXTHV7CK8Lw)Vp)M;HsdU&lzN^inG14_?*GPElI`br zs*|{h2k{V&n^WNj!|0ceJw^(hfxA-MC~`nlJ^oAZ@L@*ANz1GZRo+G|@m+J!^lJx~ zOLgEhQD2p1u(*xnLkgHrwJ&EzOz4XF0WijD6aSVXkQs|ae@W!&(Y4?2*GQNgx`Bb_ ziY1kX8}$H?0q2NpVk`sfLjBxbnM0Mq+nf{#F!_GnWeKLRsQZparMjE_W(f zr0U6}R=yodDWAj6+JQQr7khAt^>ypDGag}3?Wy);HchiltE0DqLrSOo<_ie#$kfWi zEQRze7d%Qx1C7J`HnS5rmI+;rk#)Kh^i+H-bfW3*sHfyVNvpjAWdqNoJZhW)fF3J~ zjF2}Vb>JhHw2e_JIbO1xCj@+^JM-C_3gWHd6+XT1o z1t0|Z)VOcB0o$#J!}-*>c3@b1-1@TV!o)dF!)S|}3XJ_mBCSSKe8~r)hK>g3Zx{0Q zC2jkpc-L4nrVg~U8h3)cyay#zOpz#82CkHM^9YF0bnDiw091}yK8w~N9anAFa#%gqqspO{c&AH8fF&vp9v+a$)%Lu~dPPO&nM5U8&P~@W=(cTp z<)v`AM@BjCG00k?KId9?o(2>1F+nwr|UArP)DG`SUoU4xD(=qk5!!# zcFul|M*0KP>wGm#J+@<_YjRm{J4Y#XiWDk_)6m+SxVDgVe=zaD?B~8NsUVr3a!E4lL}JbtxV-Bp}q^^a#~66*E@&owp|6? zjF{D4>k`jWn+7*;{yQ+XaA5ZrWto=kSWXoRy__lsWdsKc-;bQzVtb+v>U}8xw_j9=6nkA7?g{UWXT#)JlNi74c$J& zi`;pIacSPP{2pQZC;zs@e-;jgkHrhIlY5XUR#jP(t-{TXd0IkkG2W%n?#kYz6u@M9 z&K~_Sui1%CN%q+i4zZFBep#s${V~OerIlOz7WPYC9T3~Cu!Ey)tO+U>}QiK1xN8m zvDFf5l&nDt_cW46niyJ?SPLSN-mLSN8GphxCuRTgruMjRuz7JIRa9s$Dy$K<@~AB; z9X_mRv(%c^kKdNA>Whw9aP`^j5<6`#^)P&SUi#e~T*dF-JUCf`yTkD2yE}QSMTw1V zW@QndDso3EWg9<8%g1gVL6^~n&8$5d<25LVj%5mkWkO*~v4=nboGbNR2Bf{Y1jL|R z+}`RF{{FeCok2>R+MZDJnHX^lrgYsH>uW4yh2~3FJw!#SzEwm|FWm_AsxST)6cIW5 zwN8TgwltH-0qPH4yiVbIBW3jY_jV%35q%yAi_MA|KhyN@4FzqxVc8P`17`SFo#vkn TLQrgPHGkj5tRC0-X0zM literal 0 HcmV?d00001 diff --git a/forge/loader/src/main/resources/pack.mcmeta b/forge/loader/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..fc492580a --- /dev/null +++ b/forge/loader/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "LuckPerms resources", + "pack_format": 8 + } +} \ No newline at end of file diff --git a/forge/src/main/java/me/lucko/luckperms/forge/ForgeCommandExecutor.java b/forge/src/main/java/me/lucko/luckperms/forge/ForgeCommandExecutor.java new file mode 100644 index 000000000..4f959c6c1 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/ForgeCommandExecutor.java @@ -0,0 +1,110 @@ +/* + * 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.forge; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.tree.ArgumentCommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; + +import me.lucko.luckperms.common.command.BrigadierCommandExecutor; +import me.lucko.luckperms.common.sender.Sender; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +import java.util.List; +import java.util.ListIterator; + +public class ForgeCommandExecutor extends BrigadierCommandExecutor { + + private final LPForgePlugin plugin; + + public ForgeCommandExecutor(LPForgePlugin plugin) { + super(plugin); + this.plugin = plugin; + } + + @SubscribeEvent + public void onRegisterCommands(RegisterCommandsEvent event) { + for (String alias : COMMAND_ALIASES) { + LiteralCommandNode command = Commands.literal(alias).executes(this).build(); + ArgumentCommandNode argument = Commands.argument("args", StringArgumentType.greedyString()) + .suggests(this) + .executes(this) + .build(); + + command.addChild(argument); + event.getDispatcher().getRoot().addChild(command); + } + } + + @Override + public Sender getSender(CommandSourceStack source) { + return this.plugin.getSenderFactory().wrap(source); + } + + @Override + public List resolveSelectors(CommandSourceStack source, List args) { + // usage of @ selectors requires at least level 2 permission + CommandSourceStack atAllowedSource = source.hasPermission(2) ? source : source.withPermission(2); + for (ListIterator it = args.listIterator(); it.hasNext(); ) { + String arg = it.next(); + if (arg.isEmpty() || arg.charAt(0) != '@') { + continue; + } + + List matchedPlayers; + try { + matchedPlayers = EntityArgument.entities().parse(new StringReader(arg)).findPlayers(atAllowedSource); + } catch (CommandSyntaxException e) { + this.plugin.getLogger().warn("Error parsing selector '" + arg + "' for " + source + " executing " + args, e); + continue; + } + + if (matchedPlayers.isEmpty()) { + continue; + } + + if (matchedPlayers.size() > 1) { + this.plugin.getLogger().warn("Error parsing selector '" + arg + "' for " + source + " executing " + args + + ": ambiguous result (more than one player matched) - " + matchedPlayers); + continue; + } + + ServerPlayer player = matchedPlayers.get(0); + it.set(player.getStringUUID()); + } + + return args; + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/ForgeConfigAdapter.java b/forge/src/main/java/me/lucko/luckperms/forge/ForgeConfigAdapter.java new file mode 100644 index 000000000..a5d431a81 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/ForgeConfigAdapter.java @@ -0,0 +1,47 @@ +/* + * 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.forge; + +import me.lucko.luckperms.common.config.generic.adapter.ConfigurateConfigAdapter; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.hocon.HoconConfigurationLoader; +import ninja.leaping.configurate.loader.ConfigurationLoader; + +import java.nio.file.Path; + +public class ForgeConfigAdapter extends ConfigurateConfigAdapter { + public ForgeConfigAdapter(LuckPermsPlugin plugin, Path path) { + super(plugin, path); + } + + @Override + protected ConfigurationLoader createLoader(Path path) { + return HoconConfigurationLoader.builder().setPath(path).build(); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/ForgeEventBus.java b/forge/src/main/java/me/lucko/luckperms/forge/ForgeEventBus.java new file mode 100644 index 000000000..18228958b --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/ForgeEventBus.java @@ -0,0 +1,50 @@ +/* + * 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.forge; + +import me.lucko.luckperms.common.api.LuckPermsApiProvider; +import me.lucko.luckperms.common.event.AbstractEventBus; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModList; + +public class ForgeEventBus extends AbstractEventBus { + public ForgeEventBus(LuckPermsPlugin plugin, LuckPermsApiProvider apiProvider) { + super(plugin, apiProvider); + } + + @Override + protected ModContainer checkPlugin(Object mod) throws IllegalArgumentException { + ModContainer modContainer = ModList.get().getModContainerByObject(mod).orElse(null); + if (modContainer != null) { + return modContainer; + } + + throw new IllegalArgumentException("Object " + mod + " (" + mod.getClass().getName() + ") is not a ModContainer."); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/ForgeSchedulerAdapter.java b/forge/src/main/java/me/lucko/luckperms/forge/ForgeSchedulerAdapter.java new file mode 100644 index 000000000..9d3d76cd4 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/ForgeSchedulerAdapter.java @@ -0,0 +1,45 @@ +/* + * 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.forge; + +import me.lucko.luckperms.common.plugin.scheduler.AbstractJavaScheduler; + +import java.util.concurrent.Executor; + +public class ForgeSchedulerAdapter extends AbstractJavaScheduler { + private final Executor sync; + + public ForgeSchedulerAdapter(LPForgeBootstrap bootstrap) { + super(bootstrap); + this.sync = r -> bootstrap.getServer().orElseThrow(() -> new IllegalStateException("Server not ready")).executeBlocking(r); + } + + @Override + public Executor sync() { + return this.sync; + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/ForgeSenderFactory.java b/forge/src/main/java/me/lucko/luckperms/forge/ForgeSenderFactory.java new file mode 100644 index 000000000..d6a364037 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/ForgeSenderFactory.java @@ -0,0 +1,116 @@ +/* + * 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.forge; + +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.locale.TranslationManager; +import me.lucko.luckperms.common.query.QueryOptionsImpl; +import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.sender.SenderFactory; +import me.lucko.luckperms.common.verbose.VerboseCheckTarget; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; +import me.lucko.luckperms.forge.capabilities.UserCapability; +import me.lucko.luckperms.forge.capabilities.UserCapabilityImpl; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.luckperms.api.util.Tristate; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; + +import java.util.Locale; +import java.util.UUID; + +public class ForgeSenderFactory extends SenderFactory { + public ForgeSenderFactory(LPForgePlugin plugin) { + super(plugin); + } + + @Override + protected UUID getUniqueId(CommandSourceStack commandSource) { + if (commandSource.getEntity() instanceof Player) { + return commandSource.getEntity().getUUID(); + } + return Sender.CONSOLE_UUID; + } + + @Override + protected String getName(CommandSourceStack commandSource) { + if (commandSource.getEntity() instanceof Player) { + return commandSource.getTextName(); + } + return Sender.CONSOLE_NAME; + } + + @Override + protected void sendMessage(CommandSourceStack sender, Component message) { + Locale locale; + if (sender.getEntity() instanceof ServerPlayer) { + ServerPlayer player = (ServerPlayer) sender.getEntity(); + UserCapabilityImpl user = UserCapabilityImpl.get(player); + locale = user.getLocale(player); + } else { + locale = null; + } + + sender.sendSuccess(toNativeText(TranslationManager.render(message, locale)), false); + } + + @Override + protected Tristate getPermissionValue(CommandSourceStack commandSource, String node) { + if (commandSource.getEntity() instanceof ServerPlayer) { + ServerPlayer player = (ServerPlayer) commandSource.getEntity(); + UserCapability user = UserCapabilityImpl.get(player); + return user.checkPermission(node); + } + + VerboseCheckTarget target = VerboseCheckTarget.internal(commandSource.getTextName()); + getPlugin().getVerboseHandler().offerPermissionCheckEvent(CheckOrigin.PLATFORM_API_HAS_PERMISSION, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, node, TristateResult.UNDEFINED); + getPlugin().getPermissionRegistry().offer(node); + return Tristate.UNDEFINED; + } + + @Override + protected boolean hasPermission(CommandSourceStack commandSource, String node) { + return getPermissionValue(commandSource, node).asBoolean(); + } + + @Override + protected void performCommand(CommandSourceStack sender, String command) { + sender.getServer().getCommands().performCommand(sender, command); + } + + @Override + protected boolean isConsole(CommandSourceStack sender) { + return !(sender.getEntity() instanceof Player); + } + + public static net.minecraft.network.chat.Component toNativeText(Component component) { + return net.minecraft.network.chat.Component.Serializer.fromJson(GsonComponentSerializer.gson().serialize(component)); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/LPForgeBootstrap.java b/forge/src/main/java/me/lucko/luckperms/forge/LPForgeBootstrap.java new file mode 100644 index 000000000..54bad0744 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/LPForgeBootstrap.java @@ -0,0 +1,290 @@ +/* + * 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.forge; + +import com.mojang.authlib.GameProfile; + +import me.lucko.luckperms.common.loader.LoaderBootstrap; +import me.lucko.luckperms.common.plugin.bootstrap.BootstrappedWithLoader; +import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; +import me.lucko.luckperms.common.plugin.classpath.ClassPathAppender; +import me.lucko.luckperms.common.plugin.classpath.JarInJarClassPathAppender; +import me.lucko.luckperms.common.plugin.logging.Log4jPluginLogger; +import me.lucko.luckperms.common.plugin.logging.PluginLogger; +import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; +import me.lucko.luckperms.forge.util.ForgeEventBusFacade; + +import net.luckperms.api.platform.Platform; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; +import net.minecraftforge.event.server.ServerAboutToStartEvent; +import net.minecraftforge.event.server.ServerStoppingEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.FMLPaths; +import net.minecraftforge.forgespi.language.IModInfo; + +import org.apache.logging.log4j.LogManager; +import org.apache.maven.artifact.versioning.ArtifactVersion; + +import java.nio.file.Path; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.function.Supplier; + +/** + * Bootstrap plugin for LuckPerms running on Forge. + */ +public final class LPForgeBootstrap implements LuckPermsBootstrap, LoaderBootstrap, BootstrappedWithLoader { + public static final String ID = "luckperms"; + + /** + * The plugin loader + */ + private final Supplier loader; + + /** + * The plugin logger + */ + private final PluginLogger logger; + + /** + * A scheduler adapter for the platform + */ + private final SchedulerAdapter schedulerAdapter; + + /** + * The plugin class path appender + */ + private final ClassPathAppender classPathAppender; + + /** + * A facade for the forge event bus, compatible with LP's jar-in-jar packaging + */ + private final ForgeEventBusFacade forgeEventBus; + + /** + * The plugin instance + */ + private final LPForgePlugin plugin; + + /** + * The time when the plugin was enabled + */ + private Instant startTime; + + // load/enable latches + private final CountDownLatch loadLatch = new CountDownLatch(1); + private final CountDownLatch enableLatch = new CountDownLatch(1); + + /** + * The Minecraft server instance + */ + private MinecraftServer server; + + public LPForgeBootstrap(Supplier loader) { + this.loader = loader; + this.logger = new Log4jPluginLogger(LogManager.getLogger(LPForgeBootstrap.ID)); + this.schedulerAdapter = new ForgeSchedulerAdapter(this); + this.classPathAppender = new JarInJarClassPathAppender(getClass().getClassLoader()); + this.forgeEventBus = new ForgeEventBusFacade(); + this.plugin = new LPForgePlugin(this); + } + + // provide adapters + + @Override + public Object getLoader() { + return this.loader; + } + + @Override + public PluginLogger getPluginLogger() { + return this.logger; + } + + @Override + public SchedulerAdapter getScheduler() { + return this.schedulerAdapter; + } + + @Override + public ClassPathAppender getClassPathAppender() { + return this.classPathAppender; + } + + public void registerListeners(Object target) { + this.forgeEventBus.register(target); + } + + // lifecycle + + @Override + public void onLoad() { // called by the loader on FMLCommonSetupEvent + this.startTime = Instant.now(); + try { + this.plugin.load(); + } finally { + this.loadLatch.countDown(); + } + + this.forgeEventBus.register(this); + this.plugin.registerEarlyListeners(); + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onServerAboutToStart(ServerAboutToStartEvent event) { + this.server = event.getServer(); + try { + this.plugin.enable(); + } finally { + this.enableLatch.countDown(); + } + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public void onServerStopping(ServerStoppingEvent event) { + this.plugin.disable(); + this.forgeEventBus.unregisterAll(); + this.server = null; + } + + @Override + public CountDownLatch getLoadLatch() { + return this.loadLatch; + } + + @Override + public CountDownLatch getEnableLatch() { + return this.enableLatch; + } + + // MinecraftServer singleton getter + + public Optional getServer() { + return Optional.ofNullable(this.server); + } + + // provide information about the plugin + + @Override + public String getVersion() { + return "@version@"; + } + + @Override + public Instant getStartupTime() { + return this.startTime; + } + + // provide information about the platform + + @Override + public Platform.Type getType() { + return Platform.Type.FORGE; + } + + @Override + public String getServerBrand() { + return ModList.get().getModContainerById("forge") + .map(ModContainer::getModInfo) + .map(IModInfo::getDisplayName) + .orElse("null"); + } + + @Override + public String getServerVersion() { + String forgeVersion = ModList.get().getModContainerById("forge") + .map(ModContainer::getModInfo) + .map(IModInfo::getVersion) + .map(ArtifactVersion::toString) + .orElse("null"); + + return getServer().map(MinecraftServer::getServerVersion).orElse("null") + "-" + forgeVersion; + } + + @Override + public Path getDataDirectory() { + return FMLPaths.CONFIGDIR.get().resolve(LPForgeBootstrap.ID).toAbsolutePath(); + } + + @Override + public Optional getPlayer(UUID uniqueId) { + return getServer().map(MinecraftServer::getPlayerList).map(playerList -> playerList.getPlayer(uniqueId)); + } + + @Override + public Optional lookupUniqueId(String username) { + return getServer().map(MinecraftServer::getProfileCache).flatMap(profileCache -> profileCache.get(username)).map(GameProfile::getId); + } + + @Override + public Optional lookupUsername(UUID uniqueId) { + return getServer().map(MinecraftServer::getProfileCache).flatMap(profileCache -> profileCache.get(uniqueId)).map(GameProfile::getName); + } + + @Override + public int getPlayerCount() { + return getServer().map(MinecraftServer::getPlayerCount).orElse(0); + } + + @Override + public Collection getPlayerList() { + return getServer().map(MinecraftServer::getPlayerList).map(PlayerList::getPlayers).map(players -> { + List list = new ArrayList<>(players.size()); + for (ServerPlayer player : players) { + list.add(player.getGameProfile().getName()); + } + return list; + }).orElse(Collections.emptyList()); + } + + @Override + public Collection getOnlinePlayers() { + return getServer().map(MinecraftServer::getPlayerList).map(PlayerList::getPlayers).map(players -> { + List list = new ArrayList<>(players.size()); + for (ServerPlayer player : players) { + list.add(player.getGameProfile().getId()); + } + return list; + }).orElse(Collections.emptyList()); + } + + @Override + public boolean isPlayerOnline(UUID uniqueId) { + return getServer().map(MinecraftServer::getPlayerList).map(playerList -> playerList.getPlayer(uniqueId)).isPresent(); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/LPForgePlugin.java b/forge/src/main/java/me/lucko/luckperms/forge/LPForgePlugin.java new file mode 100644 index 000000000..0e2326cdf --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/LPForgePlugin.java @@ -0,0 +1,249 @@ +/* + * 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.forge; + +import me.lucko.luckperms.common.api.LuckPermsApiProvider; +import me.lucko.luckperms.common.calculator.CalculatorFactory; +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter; +import me.lucko.luckperms.common.dependencies.Dependency; +import me.lucko.luckperms.common.event.AbstractEventBus; +import me.lucko.luckperms.common.locale.TranslationManager; +import me.lucko.luckperms.common.messaging.MessagingFactory; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.model.manager.group.StandardGroupManager; +import me.lucko.luckperms.common.model.manager.track.StandardTrackManager; +import me.lucko.luckperms.common.model.manager.user.StandardUserManager; +import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin; +import me.lucko.luckperms.common.sender.DummyConsoleSender; +import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.forge.calculator.ForgeCalculatorFactory; +import me.lucko.luckperms.forge.capabilities.UserCapabilityListener; +import me.lucko.luckperms.forge.context.ForgeContextManager; +import me.lucko.luckperms.forge.context.ForgePlayerCalculator; +import me.lucko.luckperms.forge.listeners.ForgeAutoOpListener; +import me.lucko.luckperms.forge.listeners.ForgeCommandListUpdater; +import me.lucko.luckperms.forge.listeners.ForgeConnectionListener; +import me.lucko.luckperms.forge.listeners.ForgePlatformListener; +import me.lucko.luckperms.forge.messaging.ForgeMessagingFactory; +import me.lucko.luckperms.forge.service.ForgePermissionHandlerListener; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.query.QueryOptions; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.players.PlayerList; +import net.minecraftforge.fml.ModContainer; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +/** + * LuckPerms implementation for Forge. + */ +public class LPForgePlugin extends AbstractLuckPermsPlugin { + private final LPForgeBootstrap bootstrap; + + private ForgeSenderFactory senderFactory; + private ForgeConnectionListener connectionListener; + private ForgeCommandExecutor commandManager; + private StandardUserManager userManager; + private StandardGroupManager groupManager; + private StandardTrackManager trackManager; + private ForgeContextManager contextManager; + + public LPForgePlugin(LPForgeBootstrap bootstrap) { + this.bootstrap = bootstrap; + } + + @Override + public LPForgeBootstrap getBootstrap() { + return this.bootstrap; + } + + protected void registerEarlyListeners() { + this.connectionListener = new ForgeConnectionListener(this); + this.bootstrap.registerListeners(this.connectionListener); + + ForgePlatformListener platformListener = new ForgePlatformListener(this); + this.bootstrap.registerListeners(platformListener); + + UserCapabilityListener userCapabilityListener = new UserCapabilityListener(); + this.bootstrap.registerListeners(userCapabilityListener); + + ForgePermissionHandlerListener permissionHandlerListener = new ForgePermissionHandlerListener(this); + this.bootstrap.registerListeners(permissionHandlerListener); + + this.commandManager = new ForgeCommandExecutor(this); + this.bootstrap.registerListeners(this.commandManager); + } + + @Override + protected void setupSenderFactory() { + this.senderFactory = new ForgeSenderFactory(this); + } + + @Override + protected Set getGlobalDependencies() { + Set dependencies = super.getGlobalDependencies(); + dependencies.add(Dependency.CONFIGURATE_CORE); + dependencies.add(Dependency.CONFIGURATE_HOCON); + dependencies.add(Dependency.HOCON_CONFIG); + return dependencies; + } + + @Override + protected ConfigurationAdapter provideConfigurationAdapter() { + return new ForgeConfigAdapter(this, resolveConfig("luckperms.conf")); + } + + @Override + protected void registerPlatformListeners() { + // Too late for Forge, registered in #registerEarlyListeners + } + + @Override + protected MessagingFactory provideMessagingFactory() { + return new ForgeMessagingFactory(this); + } + + @Override + protected void registerCommands() { + // Too late for Forge, registered in #registerEarlyListeners + } + + @Override + protected void setupManagers() { + this.userManager = new StandardUserManager(this); + this.groupManager = new StandardGroupManager(this); + this.trackManager = new StandardTrackManager(this); + } + + @Override + protected CalculatorFactory provideCalculatorFactory() { + return new ForgeCalculatorFactory(this); + } + + @Override + protected void setupContextManager() { + this.contextManager = new ForgeContextManager(this); + + ForgePlayerCalculator playerCalculator = new ForgePlayerCalculator(this, getConfiguration().get(ConfigKeys.DISABLED_CONTEXTS)); + this.bootstrap.registerListeners(playerCalculator); + this.contextManager.registerCalculator(playerCalculator); + } + + @Override + protected void setupPlatformHooks() { + } + + @Override + protected AbstractEventBus provideEventBus(LuckPermsApiProvider provider) { + return new ForgeEventBus(this, provider); + } + + @Override + protected void registerApiOnPlatform(LuckPerms api) { + } + + @Override + protected void performFinalSetup() { + // register autoop listener + if (getConfiguration().get(ConfigKeys.AUTO_OP)) { + getApiProvider().getEventBus().subscribe(new ForgeAutoOpListener(this)); + } + + // register forge command list updater + if (getConfiguration().get(ConfigKeys.UPDATE_CLIENT_COMMAND_LIST)) { + getApiProvider().getEventBus().subscribe(new ForgeCommandListUpdater(this)); + } + } + + @Override + public Optional getQueryOptionsForUser(User user) { + return this.bootstrap.getPlayer(user.getUniqueId()).map(player -> this.contextManager.getQueryOptions(player)); + } + + @Override + public Stream getOnlineSenders() { + return Stream.concat( + Stream.of(getConsoleSender()), + this.bootstrap.getServer() + .map(MinecraftServer::getPlayerList) + .map(PlayerList::getPlayers) + .map(players -> players.stream().map(player -> this.senderFactory.wrap(player.createCommandSourceStack()))).orElseGet(Stream::empty) + ); + } + + @Override + public Sender getConsoleSender() { + return this.bootstrap.getServer() + .map(server -> this.senderFactory.wrap(server.createCommandSourceStack())) + .orElseGet(() -> new DummyConsoleSender(this) { + @Override + public void sendMessage(Component message) { + LPForgePlugin.this.bootstrap.getPluginLogger().info(PlainTextComponentSerializer.plainText().serialize(TranslationManager.render(message))); + } + }); + } + + public ForgeSenderFactory getSenderFactory() { + return this.senderFactory; + } + + @Override + public ForgeConnectionListener getConnectionListener() { + return this.connectionListener; + } + + @Override + public ForgeCommandExecutor getCommandManager() { + return this.commandManager; + } + + @Override + public StandardUserManager getUserManager() { + return this.userManager; + } + + @Override + public StandardGroupManager getGroupManager() { + return this.groupManager; + } + + @Override + public StandardTrackManager getTrackManager() { + return this.trackManager; + } + + @Override + public ForgeContextManager getContextManager() { + return this.contextManager; + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/calculator/ForgeCalculatorFactory.java b/forge/src/main/java/me/lucko/luckperms/forge/calculator/ForgeCalculatorFactory.java new file mode 100644 index 000000000..b4450dade --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/calculator/ForgeCalculatorFactory.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.forge.calculator; + +import me.lucko.luckperms.common.cacheddata.CacheMetadata; +import me.lucko.luckperms.common.calculator.CalculatorFactory; +import me.lucko.luckperms.common.calculator.PermissionCalculator; +import me.lucko.luckperms.common.calculator.processor.DirectProcessor; +import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; +import me.lucko.luckperms.common.calculator.processor.RegexProcessor; +import me.lucko.luckperms.common.calculator.processor.SpongeWildcardProcessor; +import me.lucko.luckperms.common.calculator.processor.WildcardProcessor; +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.forge.LPForgePlugin; +import me.lucko.luckperms.forge.context.ForgeContextManager; + +import net.luckperms.api.query.QueryOptions; + +import java.util.ArrayList; +import java.util.List; + +public class ForgeCalculatorFactory implements CalculatorFactory { + private final LPForgePlugin plugin; + + public ForgeCalculatorFactory(LPForgePlugin plugin) { + this.plugin = plugin; + } + + @Override + public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metadata) { + List processors = new ArrayList<>(5); + + processors.add(new DirectProcessor()); + + if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) { + processors.add(new RegexProcessor()); + } + + if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) { + processors.add(new WildcardProcessor()); + } + + if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS_SPONGE)) { + processors.add(new SpongeWildcardProcessor()); + } + + boolean integratedOwner = queryOptions.option(ForgeContextManager.INTEGRATED_SERVER_OWNER).orElse(false); + if (integratedOwner && this.plugin.getConfiguration().get(ConfigKeys.INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS)) { + processors.add(ServerOwnerProcessor.INSTANCE); + } + + return new PermissionCalculator(this.plugin, metadata, processors); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/calculator/ServerOwnerProcessor.java b/forge/src/main/java/me/lucko/luckperms/forge/calculator/ServerOwnerProcessor.java new file mode 100644 index 000000000..a78471135 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/calculator/ServerOwnerProcessor.java @@ -0,0 +1,52 @@ +/* + * 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.forge.calculator; + +import me.lucko.luckperms.common.cacheddata.result.TristateResult; +import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor; +import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; + +import net.luckperms.api.util.Tristate; + +/** + * Permission processor which is added to the owner of an Integrated server to + * simply return true if no other processors match. + */ +public class ServerOwnerProcessor extends AbstractPermissionProcessor implements PermissionProcessor { + private static final TristateResult TRUE_RESULT = new TristateResult.Factory(ServerOwnerProcessor.class).result(Tristate.TRUE); + + public static final ServerOwnerProcessor INSTANCE = new ServerOwnerProcessor(); + + private ServerOwnerProcessor() { + + } + + @Override + public TristateResult hasPermission(String permission) { + return TRUE_RESULT; + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapabilityImpl.java b/forge/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapabilityImpl.java new file mode 100644 index 000000000..17a55f3c8 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapabilityImpl.java @@ -0,0 +1,149 @@ +/* + * 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.forge.capabilities; + +import me.lucko.luckperms.common.cacheddata.type.PermissionCache; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; +import me.lucko.luckperms.common.locale.TranslationManager; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; +import me.lucko.luckperms.forge.context.ForgeContextManager; + +import net.luckperms.api.query.QueryOptions; +import net.luckperms.api.util.Tristate; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Locale; + +public class UserCapabilityImpl implements UserCapability { + + /** + * Gets a {@link UserCapability} for a given {@link ServerPlayer}. + * + * @param player the player + * @return the capability + */ + public static @NotNull UserCapabilityImpl get(@NotNull Player player) { + return (UserCapabilityImpl) player.getCapability(CAPABILITY) + .orElseThrow(() -> new IllegalStateException("Capability missing for " + player.getUUID())); + } + + /** + * Gets a {@link UserCapability} for a given {@link ServerPlayer}. + * + * @param player the player + * @return the capability, or null + */ + public static @Nullable UserCapabilityImpl getNullable(@NotNull Player player) { + return (UserCapabilityImpl) player.getCapability(CAPABILITY).resolve().orElse(null); + } + + private boolean initialised = false; + + private User user; + private QueryOptionsCache queryOptionsCache; + private String language; + private Locale locale; + + public UserCapabilityImpl() { + + } + + public void initialise(UserCapabilityImpl previous) { + this.user = previous.user; + this.queryOptionsCache = previous.queryOptionsCache; + this.language = previous.language; + this.locale = previous.locale; + this.initialised = true; + } + + public void initialise(User user, ServerPlayer player, ForgeContextManager contextManager) { + this.user = user; + this.queryOptionsCache = new QueryOptionsCache<>(player, contextManager); + this.initialised = true; + } + + private void assertInitialised() { + if (!this.initialised) { + throw new IllegalStateException("Capability has not been initialised"); + } + } + + @Override + public Tristate checkPermission(String permission) { + assertInitialised(); + + if (permission == null) { + throw new NullPointerException("permission"); + } + + return checkPermission(permission, this.queryOptionsCache.getQueryOptions()); + } + + @Override + public Tristate checkPermission(String permission, QueryOptions queryOptions) { + assertInitialised(); + + if (permission == null) { + throw new NullPointerException("permission"); + } + + if (queryOptions == null) { + throw new NullPointerException("queryOptions"); + } + + PermissionCache cache = this.user.getCachedData().getPermissionData(queryOptions); + return cache.checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result(); + } + + public User getUser() { + assertInitialised(); + return this.user; + } + + @Override + public QueryOptions getQueryOptions() { + return getQueryOptionsCache().getQueryOptions(); + } + + public QueryOptionsCache getQueryOptionsCache() { + assertInitialised(); + return this.queryOptionsCache; + } + + public Locale getLocale(ServerPlayer player) { + if (this.language == null || !this.language.equals(player.getLanguage())) { + this.language = player.getLanguage(); + this.locale = TranslationManager.parseLocale(this.language); + } + + return this.locale; + } +} \ No newline at end of file diff --git a/forge/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapabilityListener.java b/forge/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapabilityListener.java new file mode 100644 index 000000000..fdbc06555 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/capabilities/UserCapabilityListener.java @@ -0,0 +1,99 @@ +/* + * 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.forge.capabilities; + +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class UserCapabilityListener { + + @SubscribeEvent + public void onRegisterCapabilities(RegisterCapabilitiesEvent event) { + event.register(UserCapabilityImpl.class); + } + + @SubscribeEvent + public void onAttachCapabilities(AttachCapabilitiesEvent event) { + if (!(event.getObject() instanceof ServerPlayer)) { + return; + } + + event.addCapability(UserCapability.IDENTIFIER, new UserCapabilityProvider(new UserCapabilityImpl())); + } + + @SubscribeEvent + public void onPlayerClone(PlayerEvent.Clone event) { + if (!event.isWasDeath()) { + return; + } + + Player previousPlayer = event.getOriginal(); + Player currentPlayer = event.getPlayer(); + + previousPlayer.reviveCaps(); + try { + UserCapabilityImpl previous = UserCapabilityImpl.get(previousPlayer); + UserCapabilityImpl current = UserCapabilityImpl.get(currentPlayer); + + current.initialise(previous); + current.getQueryOptionsCache().invalidate(); + } finally { + previousPlayer.invalidateCaps(); + } + } + + private static final class UserCapabilityProvider implements ICapabilityProvider { + private final UserCapabilityImpl userCapability; + + private UserCapabilityProvider(UserCapabilityImpl userCapability) { + this.userCapability = userCapability; + } + + @SuppressWarnings("unchecked") + @NotNull + @Override + public LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { + if (cap != UserCapability.CAPABILITY) { + return LazyOptional.empty(); + } + + return LazyOptional.of(() -> (T) this.userCapability); + } + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/context/ForgeContextManager.java b/forge/src/main/java/me/lucko/luckperms/forge/context/ForgeContextManager.java new file mode 100644 index 000000000..2a10fe4cf --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/context/ForgeContextManager.java @@ -0,0 +1,80 @@ +/* + * 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.forge.context; + +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.context.manager.ContextManager; +import me.lucko.luckperms.common.context.manager.QueryOptionsCache; +import me.lucko.luckperms.forge.LPForgePlugin; +import me.lucko.luckperms.forge.capabilities.UserCapabilityImpl; + +import net.luckperms.api.context.ImmutableContextSet; +import net.luckperms.api.query.OptionKey; +import net.luckperms.api.query.QueryOptions; +import net.minecraft.server.level.ServerPlayer; + +import java.util.UUID; + +public class ForgeContextManager extends ContextManager { + public static final OptionKey INTEGRATED_SERVER_OWNER = OptionKey.of("integrated_server_owner", Boolean.class); + + public ForgeContextManager(LPForgePlugin plugin) { + super(plugin, ServerPlayer.class, ServerPlayer.class); + } + + @Override + public UUID getUniqueId(ServerPlayer player) { + return player.getUUID(); + } + + @Override + public QueryOptionsCache getCacheFor(ServerPlayer subject) { + if (subject == null) { + throw new NullPointerException("subject"); + } + + return UserCapabilityImpl.get(subject).getQueryOptionsCache(); + } + + @Override + public QueryOptions formQueryOptions(ServerPlayer subject, ImmutableContextSet contextSet) { + QueryOptions.Builder builder = this.plugin.getConfiguration().get(ConfigKeys.GLOBAL_QUERY_OPTIONS).toBuilder(); + if (subject.getServer() != null && subject.getServer().isSingleplayerOwner(subject.getGameProfile())) { + builder.option(INTEGRATED_SERVER_OWNER, true); + } + + return builder.context(contextSet).build(); + } + + @Override + public void invalidateCache(ServerPlayer subject) { + UserCapabilityImpl capability = UserCapabilityImpl.getNullable(subject); + if (capability != null) { + capability.getQueryOptionsCache().invalidate(); + } + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/context/ForgePlayerCalculator.java b/forge/src/main/java/me/lucko/luckperms/forge/context/ForgePlayerCalculator.java new file mode 100644 index 000000000..61c86cd29 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/context/ForgePlayerCalculator.java @@ -0,0 +1,149 @@ +/* + * 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.forge.context; + +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.forge.LPForgePlugin; + +import net.luckperms.api.context.Context; +import net.luckperms.api.context.ContextCalculator; +import net.luckperms.api.context.ContextConsumer; +import net.luckperms.api.context.ContextSet; +import net.luckperms.api.context.DefaultContextKeys; +import net.luckperms.api.context.ImmutableContextSet; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.storage.ServerLevelData; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Set; + +public class ForgePlayerCalculator implements ContextCalculator { + /** + * GameType.NOT_SET(-1, "") was removed in 1.17 + */ + private static final int GAME_MODE_NOT_SET = -1; + + private final LPForgePlugin plugin; + + private final boolean gamemode; + private final boolean world; + private final boolean dimensionType; + + public ForgePlayerCalculator(LPForgePlugin plugin, Set disabled) { + this.plugin = plugin; + this.gamemode = !disabled.contains(DefaultContextKeys.GAMEMODE_KEY); + this.world = !disabled.contains(DefaultContextKeys.WORLD_KEY); + this.dimensionType = !disabled.contains(DefaultContextKeys.DIMENSION_TYPE_KEY); + } + + @Override + public void calculate(@NonNull ServerPlayer target, @NonNull ContextConsumer consumer) { + ServerLevel level = target.getLevel(); + if (this.dimensionType) { + consumer.accept(DefaultContextKeys.DIMENSION_TYPE_KEY, getContextKey(level.dimension().location())); + } + + ServerLevelData levelData = (ServerLevelData) level.getLevelData(); + if (this.world) { + this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).rewriteAndSubmit(levelData.getLevelName(), consumer); + } + + GameType gameMode = target.gameMode.getGameModeForPlayer(); + if (this.gamemode && gameMode.getId() != GAME_MODE_NOT_SET) { + consumer.accept(DefaultContextKeys.GAMEMODE_KEY, gameMode.getName()); + } + } + + @Override + public @NonNull ContextSet estimatePotentialContexts() { + ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl(); + + if (this.gamemode) { + for (GameType gameType : GameType.values()) { + if (gameType.getId() == GAME_MODE_NOT_SET) { + continue; + } + + builder.add(DefaultContextKeys.GAMEMODE_KEY, gameType.getName()); + } + } + + MinecraftServer server = this.plugin.getBootstrap().getServer().orElse(null); + if (this.dimensionType && server != null) { + server.registryAccess().registry(Registry.DIMENSION_TYPE_REGISTRY).ifPresent(registry -> { + for (ResourceLocation resourceLocation : registry.keySet()) { + builder.add(DefaultContextKeys.DIMENSION_TYPE_KEY, getContextKey(resourceLocation)); + } + }); + } + + if (this.world && server != null) { + for (ServerLevel level : server.getAllLevels()) { + ServerLevelData levelData = (ServerLevelData) level.getLevelData(); + if (Context.isValidValue(levelData.getLevelName())) { + builder.add(DefaultContextKeys.WORLD_KEY, levelData.getLevelName()); + } + } + } + + return builder.build(); + } + + private static String getContextKey(ResourceLocation key) { + if (key.getNamespace().equals("minecraft")) { + return key.getPath(); + } + return key.toString(); + } + + @SubscribeEvent + public void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) { + if (!(this.world || this.dimensionType)) { + return; + } + + this.plugin.getContextManager().signalContextUpdate((ServerPlayer) event.getPlayer()); + } + + @SubscribeEvent + public void onPlayerChangeGameMode(PlayerEvent.PlayerChangeGameModeEvent event) { + if (!this.gamemode || event.getNewGameMode().getId() == GAME_MODE_NOT_SET) { + return; + } + + this.plugin.getContextManager().signalContextUpdate((ServerPlayer) event.getPlayer()); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeAutoOpListener.java b/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeAutoOpListener.java new file mode 100644 index 000000000..fbb975002 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeAutoOpListener.java @@ -0,0 +1,98 @@ +/* + * 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.forge.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.forge.LPForgePlugin; + +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 net.minecraft.server.level.ServerPlayer; + +import java.util.Map; + +public class ForgeAutoOpListener implements LuckPermsEventListener { + private static final String NODE = "luckperms.autoop"; + + private final LPForgePlugin plugin; + + public ForgeAutoOpListener(LPForgePlugin plugin) { + this.plugin = plugin; + } + + @Override + public void bind(EventBus bus) { + bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate); + bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate); + } + + private void onContextUpdate(ContextUpdateEvent event) { + event.getSubject(ServerPlayer.class).ifPresent(player -> refreshAutoOp(player, true)); + } + + private void onUserDataRecalculate(UserDataRecalculateEvent event) { + User user = ApiUser.cast(event.getUser()); + this.plugin.getBootstrap().getPlayer(user.getUniqueId()).ifPresent(player -> refreshAutoOp(player, false)); + } + + private void refreshAutoOp(ServerPlayer player, boolean callerIsSync) { + if (!callerIsSync && !this.plugin.getBootstrap().getServer().isPresent()) { + return; + } + + User user = this.plugin.getUserManager().getIfLoaded(player.getUUID()); + + 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) { + setOp(player, value); + } else { + this.plugin.getBootstrap().getScheduler().executeSync(() -> setOp(player, value)); + } + } + + private void setOp(ServerPlayer player, boolean value) { + this.plugin.getBootstrap().getServer().ifPresent(server -> { + if (value) { + server.getPlayerList().op(player.getGameProfile()); + } else { + server.getPlayerList().deop(player.getGameProfile()); + } + }); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeCommandListUpdater.java b/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeCommandListUpdater.java new file mode 100644 index 000000000..9a534a209 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeCommandListUpdater.java @@ -0,0 +1,121 @@ +/* + * 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.forge.listeners; + +import com.github.benmanes.caffeine.cache.LoadingCache; + +import me.lucko.luckperms.common.api.implementation.ApiGroup; +import me.lucko.luckperms.common.cache.BufferedRequest; +import me.lucko.luckperms.common.event.LuckPermsEventListener; +import me.lucko.luckperms.common.util.CaffeineFactory; +import me.lucko.luckperms.forge.LPForgePlugin; + +import net.luckperms.api.event.EventBus; +import net.luckperms.api.event.context.ContextUpdateEvent; +import net.luckperms.api.event.group.GroupDataRecalculateEvent; +import net.luckperms.api.event.user.UserDataRecalculateEvent; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * Calls {@link net.minecraft.server.players.PlayerList#sendPlayerPermissionLevel(ServerPlayer)} when a players permissions change. + */ +public class ForgeCommandListUpdater implements LuckPermsEventListener { + private final LPForgePlugin plugin; + private final LoadingCache sendingBuffers = CaffeineFactory.newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .build(SendBuffer::new); + + public ForgeCommandListUpdater(LPForgePlugin plugin) { + this.plugin = plugin; + } + + @Override + public void bind(EventBus bus) { + bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate); + bus.subscribe(GroupDataRecalculateEvent.class, this::onGroupDataRecalculate); + bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate); + } + + private void onUserDataRecalculate(UserDataRecalculateEvent e) { + requestUpdate(e.getUser().getUniqueId()); + } + + private void onGroupDataRecalculate(GroupDataRecalculateEvent e) { + plugin.getUserManager().getAll().values().forEach(user -> { + if (user.resolveInheritanceTree(user.getQueryOptions()).contains(ApiGroup.cast(e.getGroup()))) { + requestUpdate(user.getUniqueId()); + } + }); + } + + private void onContextUpdate(ContextUpdateEvent e) { + e.getSubject(ServerPlayer.class).ifPresent(p -> requestUpdate(p.getUUID())); + } + + private void requestUpdate(UUID uniqueId) { + if (!this.plugin.getBootstrap().isPlayerOnline(uniqueId)) { + return; + } + + // Buffer the request to send a commands update. + SendBuffer sendBuffer = this.sendingBuffers.get(uniqueId); + if (sendBuffer != null) { + sendBuffer.request(); + } + } + + // Called when the buffer times out. + private void sendUpdate(UUID uniqueId) { + this.plugin.getBootstrap().getScheduler().sync().execute(() -> { + this.plugin.getBootstrap().getPlayer(uniqueId).ifPresent(player -> { + MinecraftServer server = player.getServer(); + if (server != null) { + server.getPlayerList().sendPlayerPermissionLevel(player); + } + }); + }); + } + + private final class SendBuffer extends BufferedRequest { + private final UUID uniqueId; + + SendBuffer(UUID uniqueId) { + super(500, TimeUnit.MILLISECONDS, ForgeCommandListUpdater.this.plugin.getBootstrap().getScheduler()); + this.uniqueId = uniqueId; + } + + @Override + protected Void perform() { + sendUpdate(this.uniqueId); + return null; + } + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeConnectionListener.java b/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeConnectionListener.java new file mode 100644 index 000000000..4bda329da --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgeConnectionListener.java @@ -0,0 +1,154 @@ +/* + * 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.forge.listeners; + +import com.mojang.authlib.GameProfile; + +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.common.locale.TranslationManager; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener; +import me.lucko.luckperms.forge.ForgeSenderFactory; +import me.lucko.luckperms.forge.LPForgePlugin; +import me.lucko.luckperms.forge.capabilities.UserCapabilityImpl; + +import net.kyori.adventure.text.Component; +import net.minecraft.Util; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.ChatType; +import net.minecraft.network.protocol.game.ClientboundChatPacket; +import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.event.entity.player.PlayerNegotiationEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class ForgeConnectionListener extends AbstractConnectionListener { + private final LPForgePlugin plugin; + + public ForgeConnectionListener(LPForgePlugin plugin) { + super(plugin); + this.plugin = plugin; + } + + @SubscribeEvent + public void onPlayerNegotiation(PlayerNegotiationEvent event) { + String username = event.getProfile().getName(); + UUID uniqueId = event.getProfile().isComplete() ? event.getProfile().getId() : Player.createPlayerUUID(username); + + if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { + this.plugin.getLogger().info("Processing pre-login (sync phase) for " + uniqueId + " - " + username); + } + + event.enqueueWork(CompletableFuture.runAsync(() -> { + onPlayerNegotiationAsync(event.getConnection(), uniqueId, username); + }, this.plugin.getBootstrap().getScheduler().async())); + } + + private void onPlayerNegotiationAsync(Connection connection, UUID uniqueId, String username) { + if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { + this.plugin.getLogger().info("Processing pre-login (async phase) for " + uniqueId + " - " + username); + } + + /* Actually process the login for the connection. + We do this here to delay the login until the data is ready. + If the login gets cancelled later on, then this will be cleaned up. + + This includes: + - loading uuid data + - loading permissions + - creating a user instance in the UserManager for this connection. + - setting up cached data. */ + try { + User user = loadUser(uniqueId, username); + recordConnection(uniqueId); + this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(uniqueId, username, user); + } catch (Exception ex) { + this.plugin.getLogger().severe("Exception occurred whilst loading data for " + uniqueId + " - " + username, ex); + + if (this.plugin.getConfiguration().get(ConfigKeys.CANCEL_FAILED_LOGINS)) { + Component component = TranslationManager.render(Message.LOADING_DATABASE_ERROR.build()); + connection.send(new ClientboundLoginDisconnectPacket(ForgeSenderFactory.toNativeText(component))); + connection.disconnect(ForgeSenderFactory.toNativeText(component)); + } else { + // Schedule the message to be sent on the next tick. + this.plugin.getBootstrap().getServer().orElseThrow(IllegalStateException::new).execute(() -> { + Component component = TranslationManager.render(Message.LOADING_STATE_ERROR.build()); + connection.send(new ClientboundChatPacket(ForgeSenderFactory.toNativeText(component), ChatType.SYSTEM, Util.NIL_UUID)); + }); + } + + this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(uniqueId, username, null); + } + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onPlayerLoadFromFile(PlayerEvent.LoadFromFile event) { + ServerPlayer player = (ServerPlayer) event.getPlayer(); + GameProfile profile = player.getGameProfile(); + + if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { + this.plugin.getLogger().info("Processing post-login for " + profile.getId() + " - " + profile.getName()); + } + + User user = this.plugin.getUserManager().getIfLoaded(profile.getId()); + + if (user == null) { + if (!getUniqueConnections().contains(profile.getId())) { + this.plugin.getLogger().warn("User " + profile.getId() + " - " + profile.getName() + + " doesn't have data pre-loaded, they have never been processed during pre-login in this session."); + } else { + this.plugin.getLogger().warn("User " + profile.getId() + " - " + profile.getName() + + " doesn't currently have data pre-loaded, but they have been processed before in this session."); + } + + Component component = TranslationManager.render(Message.LOADING_STATE_ERROR.build(), player.getLanguage()); + if (this.plugin.getConfiguration().get(ConfigKeys.CANCEL_FAILED_LOGINS)) { + player.connection.disconnect(ForgeSenderFactory.toNativeText(component)); + } else { + player.sendMessage(ForgeSenderFactory.toNativeText(component), Util.NIL_UUID); + } + } + + // initialise capability + UserCapabilityImpl userCapability = UserCapabilityImpl.get(player); + userCapability.initialise(user, player, this.plugin.getContextManager()); + this.plugin.getContextManager().signalContextUpdate(player); + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { + ServerPlayer player = (ServerPlayer) event.getPlayer(); + handleDisconnect(player.getGameProfile().getId()); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgePlatformListener.java b/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgePlatformListener.java new file mode 100644 index 000000000..22fe6385c --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/listeners/ForgePlatformListener.java @@ -0,0 +1,94 @@ +/* + * 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.forge.listeners; + +import com.mojang.brigadier.context.CommandContextBuilder; +import com.mojang.brigadier.context.ParsedCommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; + +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.locale.Message; +import me.lucko.luckperms.forge.LPForgePlugin; +import me.lucko.luckperms.forge.util.BrigadierInjector; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.players.ServerOpList; +import net.minecraftforge.event.AddReloadListenerEvent; +import net.minecraftforge.event.CommandEvent; +import net.minecraftforge.event.server.ServerStartedEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +import java.io.IOException; +import java.util.Locale; + +public class ForgePlatformListener { + private final LPForgePlugin plugin; + + public ForgePlatformListener(LPForgePlugin plugin) { + this.plugin = plugin; + } + + @SubscribeEvent + public void onCommand(CommandEvent event) { + CommandContextBuilder context = event.getParseResults().getContext(); + + if (!this.plugin.getConfiguration().get(ConfigKeys.OPS_ENABLED)) { + for (ParsedCommandNode node : context.getNodes()) { + if (!(node.getNode() instanceof LiteralCommandNode)) { + continue; + } + + String name = node.getNode().getName().toLowerCase(Locale.ROOT); + if (name.equals("op") || name.equals("deop")) { + Message.OP_DISABLED.send(this.plugin.getSenderFactory().wrap(context.getSource())); + event.setCanceled(true); + return; + } + } + } + } + + @SubscribeEvent + public void onAddReloadListener(AddReloadListenerEvent event) { + Commands commands = event.getServerResources().getCommands(); + BrigadierInjector.inject(this.plugin, commands.getDispatcher()); + } + + @SubscribeEvent + public void onServerStarted(ServerStartedEvent event) { + if (!this.plugin.getConfiguration().get(ConfigKeys.OPS_ENABLED)) { + ServerOpList ops = event.getServer().getPlayerList().getOps(); + ops.getEntries().clear(); + try { + ops.save(); + } catch (IOException ex) { + this.plugin.getLogger().severe("Encountered an error while saving ops", ex); + } + } + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/messaging/ForgeMessagingFactory.java b/forge/src/main/java/me/lucko/luckperms/forge/messaging/ForgeMessagingFactory.java new file mode 100644 index 000000000..a0c07d566 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/messaging/ForgeMessagingFactory.java @@ -0,0 +1,72 @@ +/* + * 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.forge.messaging; + +import me.lucko.luckperms.common.messaging.InternalMessagingService; +import me.lucko.luckperms.common.messaging.LuckPermsMessagingService; +import me.lucko.luckperms.common.messaging.MessagingFactory; +import me.lucko.luckperms.forge.LPForgePlugin; + +import net.luckperms.api.messenger.IncomingMessageConsumer; +import net.luckperms.api.messenger.Messenger; +import net.luckperms.api.messenger.MessengerProvider; + +import org.checkerframework.checker.nullness.qual.NonNull; + +public class ForgeMessagingFactory extends MessagingFactory { + public ForgeMessagingFactory(LPForgePlugin plugin) { + super(plugin); + } + + @Override + protected InternalMessagingService getServiceFor(String messagingType) { + if (messagingType.equals("pluginmsg") || messagingType.equals("bungee") || messagingType.equals("velocity")) { + try { + return new LuckPermsMessagingService(getPlugin(), new PluginMessageMessengerProvider()); + } catch (Exception e) { + getPlugin().getLogger().severe("Exception occurred whilst enabling messaging", e); + } + } + + return super.getServiceFor(messagingType); + } + + private class PluginMessageMessengerProvider implements MessengerProvider { + + @Override + public @NonNull String getName() { + return "PluginMessage"; + } + + @Override + public @NonNull Messenger obtain(@NonNull IncomingMessageConsumer incomingMessageConsumer) { + PluginMessageMessenger messenger = new PluginMessageMessenger(getPlugin(), incomingMessageConsumer); + messenger.init(); + return messenger; + } + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/messaging/PluginMessageMessenger.java b/forge/src/main/java/me/lucko/luckperms/forge/messaging/PluginMessageMessenger.java new file mode 100644 index 000000000..f3cda3635 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/messaging/PluginMessageMessenger.java @@ -0,0 +1,101 @@ +/* + * 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.forge.messaging; + +import com.google.common.collect.Iterables; + +import me.lucko.luckperms.common.messaging.pluginmsg.AbstractPluginMessageMessenger; +import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask; +import me.lucko.luckperms.forge.LPForgePlugin; + +import net.luckperms.api.messenger.IncomingMessageConsumer; +import net.luckperms.api.messenger.Messenger; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; +import net.minecraftforge.network.NetworkRegistry; +import net.minecraftforge.network.event.EventNetworkChannel; + +import io.netty.buffer.Unpooled; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +public class PluginMessageMessenger extends AbstractPluginMessageMessenger implements Messenger { + private static final ResourceLocation CHANNEL = new ResourceLocation(AbstractPluginMessageMessenger.CHANNEL); + + private final LPForgePlugin plugin; + private EventNetworkChannel channel; + + public PluginMessageMessenger(LPForgePlugin plugin, IncomingMessageConsumer consumer) { + super(consumer); + this.plugin = plugin; + } + + public void init() { + this.channel = NetworkRegistry.newEventChannel(CHANNEL, () -> "1", predicate -> true, predicate -> true); + this.channel.addListener(event -> { + byte[] buf = new byte[event.getPayload().readableBytes()]; + event.getPayload().readBytes(buf); + + handleIncomingMessage(buf); + event.getSource().get().setPacketHandled(true); + }); + } + + @Override + protected void sendOutgoingMessage(byte[] buf) { + AtomicReference taskRef = new AtomicReference<>(); + SchedulerTask task = this.plugin.getBootstrap().getScheduler().asyncRepeating(() -> { + ServerPlayer player = this.plugin.getBootstrap().getServer() + .map(MinecraftServer::getPlayerList) + .map(PlayerList::getPlayers) + .map(players -> Iterables.getFirst(players, null)) + .orElse(null); + + if (player == null) { + return; + } + + FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.buffer()); + byteBuf.writeBytes(buf); + Packet packet = new ClientboundCustomPayloadPacket(CHANNEL, byteBuf); + + player.connection.send(packet); + + SchedulerTask t = taskRef.getAndSet(null); + if (t != null) { + t.cancel(); + } + }, 10, TimeUnit.SECONDS); + taskRef.set(task); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java b/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java new file mode 100644 index 000000000..86ed3e20d --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java @@ -0,0 +1,155 @@ +/* + * 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.forge.service; + +import me.lucko.luckperms.common.cacheddata.type.MetaCache; +import me.lucko.luckperms.common.cacheddata.type.PermissionCache; +import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.verbose.event.CheckOrigin; +import me.lucko.luckperms.forge.LPForgeBootstrap; +import me.lucko.luckperms.forge.LPForgePlugin; +import me.lucko.luckperms.forge.capabilities.UserCapabilityImpl; + +import net.luckperms.api.context.ImmutableContextSet; +import net.luckperms.api.query.QueryMode; +import net.luckperms.api.query.QueryOptions; +import net.luckperms.api.util.Tristate; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.server.permission.handler.IPermissionHandler; +import net.minecraftforge.server.permission.nodes.PermissionDynamicContext; +import net.minecraftforge.server.permission.nodes.PermissionNode; +import net.minecraftforge.server.permission.nodes.PermissionTypes; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class ForgePermissionHandler implements IPermissionHandler { + public static final ResourceLocation IDENTIFIER = new ResourceLocation(LPForgeBootstrap.ID, "permission_handler"); + + private final LPForgePlugin plugin; + private final Set> permissionNodes; + + public ForgePermissionHandler(LPForgePlugin plugin, Collection> permissionNodes) { + this.plugin = plugin; + this.permissionNodes = Collections.unmodifiableSet(new HashSet<>(permissionNodes)); + + for (PermissionNode node : this.permissionNodes) { + this.plugin.getPermissionRegistry().insert(node.getNodeName()); + } + } + + @Override + public ResourceLocation getIdentifier() { + return IDENTIFIER; + } + + @Override + public Set> getRegisteredNodes() { + return this.permissionNodes; + } + + @Override + public T getPermission(ServerPlayer player, PermissionNode node, PermissionDynamicContext... context) { + UserCapabilityImpl capability = UserCapabilityImpl.getNullable(player); + + if (capability != null) { + User user = capability.getUser(); + QueryOptions queryOptions = capability.getQueryOptionsCache().getQueryOptions(); + + T value = getPermissionValue(user, queryOptions, node, context); + if (value != null) { + return value; + } + } + + return node.getDefaultResolver().resolve(player, player.getUUID(), context); + } + + @Override + public T getOfflinePermission(UUID player, PermissionNode node, PermissionDynamicContext... context) { + User user = this.plugin.getUserManager().getIfLoaded(player); + + if (user != null) { + QueryOptions queryOptions = user.getQueryOptions(); + T value = getPermissionValue(user, queryOptions, node, context); + if (value != null) { + return value; + } + } + + return node.getDefaultResolver().resolve(null, player, context); + } + + @SuppressWarnings("unchecked") + private static T getPermissionValue(User user, QueryOptions queryOptions, PermissionNode node, PermissionDynamicContext... context) { + queryOptions = appendContextToQueryOptions(queryOptions, context); + + if (node.getType() == PermissionTypes.BOOLEAN) { + PermissionCache cache = user.getCachedData().getPermissionData(queryOptions); + Tristate value = cache.checkPermission(node.getNodeName(), CheckOrigin.PLATFORM_API_HAS_PERMISSION).result(); + return (T) (Boolean) value.asBoolean(); + } + + if (node.getType() == PermissionTypes.INTEGER) { + MetaCache cache = user.getCachedData().getMetaData(queryOptions); + Integer value = cache.getMetaValue(node.getNodeName(), Integer::parseInt).orElse(null); + if (value != null) { + return (T) value; + } + } + + if (node.getType() == PermissionTypes.STRING) { + MetaCache cache = user.getCachedData().getMetaData(queryOptions); + String value = cache.getMetaValue(node.getNodeName()); + if (value != null) { + return (T) value; + } + } + + return null; + } + + private static QueryOptions appendContextToQueryOptions(QueryOptions queryOptions, PermissionDynamicContext... context) { + if (context.length == 0 || queryOptions.mode() != QueryMode.CONTEXTUAL) { + return queryOptions; + } + + ImmutableContextSet.Builder contextBuilder = new ImmutableContextSetImpl.BuilderImpl() + .addAll(queryOptions.context()); + + for (PermissionDynamicContext dynamicContext : context) { + contextBuilder.add(dynamicContext.getDynamic().name(), dynamicContext.getSerializedValue()); + } + + return queryOptions.toBuilder().context(contextBuilder.build()).build(); + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandlerListener.java b/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandlerListener.java new file mode 100644 index 000000000..315cb9e43 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandlerListener.java @@ -0,0 +1,65 @@ +/* + * 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.forge.service; + +import me.lucko.luckperms.common.command.access.CommandPermission; +import me.lucko.luckperms.forge.LPForgePlugin; + +import net.minecraftforge.common.ForgeConfig; +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.server.permission.events.PermissionGatherEvent; +import net.minecraftforge.server.permission.handler.DefaultPermissionHandler; +import net.minecraftforge.server.permission.nodes.PermissionNode; +import net.minecraftforge.server.permission.nodes.PermissionTypes; + +public class ForgePermissionHandlerListener { + private final LPForgePlugin plugin; + + public ForgePermissionHandlerListener(LPForgePlugin plugin) { + this.plugin = plugin; + } + + @SubscribeEvent + public void onPermissionGatherHandler(PermissionGatherEvent.Handler event) { + // Override the default permission handler with LuckPerms + ForgeConfigSpec.ConfigValue permissionHandler = ForgeConfig.SERVER.permissionHandler; + if (permissionHandler.get().equals(DefaultPermissionHandler.IDENTIFIER.toString())) { + permissionHandler.set(ForgePermissionHandler.IDENTIFIER.toString()); + } + + event.addPermissionHandler(ForgePermissionHandler.IDENTIFIER, permissions -> new ForgePermissionHandler(this.plugin, permissions)); + } + + @SubscribeEvent + public void onPermissionGatherNodes(PermissionGatherEvent.Nodes event) { + // register luckperms nodes + for (CommandPermission permission : CommandPermission.values()) { + event.addNodes(new PermissionNode<>("luckperms", permission.getNode(), PermissionTypes.BOOLEAN, (player, uuid, context) -> false)); + } + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/util/BrigadierInjector.java b/forge/src/main/java/me/lucko/luckperms/forge/util/BrigadierInjector.java new file mode 100644 index 000000000..9f27e7af7 --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/util/BrigadierInjector.java @@ -0,0 +1,186 @@ +/* + * 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.forge.util; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; + +import me.lucko.luckperms.common.graph.Graph; +import me.lucko.luckperms.common.graph.TraversalAlgorithm; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.forge.capabilities.UserCapability; +import me.lucko.luckperms.forge.capabilities.UserCapabilityImpl; + +import net.luckperms.api.util.Tristate; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Locale; +import java.util.function.Predicate; + +/** + * Utility for injecting permission requirements into a Brigadier command tree. + */ +public final class BrigadierInjector { + private BrigadierInjector() {} + + private static final Field REQUIREMENT_FIELD; + + static { + Field requirementField; + try { + requirementField = CommandNode.class.getDeclaredField("requirement"); + requirementField.setAccessible(true); + } catch (NoSuchFieldException e) { + throw new ExceptionInInitializerError(e); + } + REQUIREMENT_FIELD = requirementField; + } + + /** + * Inject permission requirements into the commands in the given dispatcher. + * + * @param plugin the plugin + * @param dispatcher the command dispatcher + */ + public static void inject(LuckPermsPlugin plugin, CommandDispatcher dispatcher) { + Iterable tree = CommandNodeGraph.INSTANCE.traverse( + TraversalAlgorithm.DEPTH_FIRST_PRE_ORDER, + new CommandNodeWithParent(null, dispatcher.getRoot()) + ); + + for (CommandNodeWithParent node : tree) { + Predicate requirement = node.node.getRequirement(); + + // already injected - skip + if (requirement instanceof InjectedPermissionRequirement) { + continue; + } + + String permission = buildPermissionNode(node); + if (permission == null) { + continue; + } + + plugin.getPermissionRegistry().insert(permission); + + InjectedPermissionRequirement newRequirement = new InjectedPermissionRequirement(permission, requirement); + try { + REQUIREMENT_FIELD.set(node.node, newRequirement); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + + private static String buildPermissionNode(CommandNodeWithParent node) { + StringBuilder builder = new StringBuilder(); + + while (node != null) { + if (node.node instanceof LiteralCommandNode) { + if (builder.length() != 0) { + builder.insert(0, '.'); + } + + String name = node.node.getName().toLowerCase(Locale.ROOT); + builder.insert(0, name); + } + + node = node.parent; + } + + if (builder.length() == 0) { + return null; + } + + builder.insert(0, "command."); + return builder.toString(); + } + + /** + * Injected {@link CommandNode#getRequirement() requirement} that checks for a permission, before + * delegating to the existing requirement. + */ + private static final class InjectedPermissionRequirement implements Predicate { + private final String permission; + private final Predicate delegate; + + private InjectedPermissionRequirement(String permission, Predicate delegate) { + this.permission = permission; + this.delegate = delegate; + } + + @Override + public boolean test(CommandSourceStack source) { + if (source.getEntity() instanceof ServerPlayer) { + ServerPlayer player = (ServerPlayer) source.getEntity(); + + UserCapability user = UserCapabilityImpl.get(player); + Tristate state = user.checkPermission(this.permission); + + if (state != Tristate.UNDEFINED) { + return state.asBoolean() && this.delegate.test(source.withPermission(4)); + } + } + + return this.delegate.test(source); + } + } + + /** + * A {@link Graph} to represent the brigadier command node tree. + */ + private enum CommandNodeGraph implements Graph { + INSTANCE; + + @Override + public Iterable successors(CommandNodeWithParent ctx) { + CommandNode node = ctx.node; + Collection successors = new ArrayList<>(); + + for (CommandNode child : node.getChildren()) { + successors.add(new CommandNodeWithParent(ctx, child)); + } + + return successors; + } + } + + private static final class CommandNodeWithParent { + private final CommandNodeWithParent parent; + private final CommandNode node; + + private CommandNodeWithParent(CommandNodeWithParent parent, CommandNode node) { + this.parent = parent; + this.node = node; + } + } + +} diff --git a/forge/src/main/java/me/lucko/luckperms/forge/util/ForgeEventBusFacade.java b/forge/src/main/java/me/lucko/luckperms/forge/util/ForgeEventBusFacade.java new file mode 100644 index 000000000..3c6ea378c --- /dev/null +++ b/forge/src/main/java/me/lucko/luckperms/forge/util/ForgeEventBusFacade.java @@ -0,0 +1,265 @@ +/* + * 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.forge.util; + +import me.lucko.luckperms.common.loader.JarInJarClassLoader; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.eventbus.api.GenericEvent; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.eventbus.api.IGenericEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.event.IModBusEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; + +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * A utility for registering Forge listeners for methods in a jar-in-jar. + * + *

    This differs from {@link IEventBus#register} as reflection is used for invoking the registered listeners + * instead of ASM, which is incompatible with {@link JarInJarClassLoader}

    + */ +public class ForgeEventBusFacade { + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + + private final List listeners = new ArrayList<>(); + + /** + * Register listeners for all methods annotated with {@link SubscribeEvent} on the target object. + */ + public void register(Object target) { + for (Method method : target.getClass().getMethods()) { + // Ignore static methods, Support for these could be added, but they are not used in LuckPerms + if (Modifier.isStatic(method.getModifiers())) { + continue; + } + + // Methods require a SubscribeEvent annotation in order to be registered + SubscribeEvent subscribeEvent = method.getAnnotation(SubscribeEvent.class); + if (subscribeEvent == null) { + continue; + } + + EventType type = determineListenerType(method); + Consumer invoker = createInvokerFunction(method, target, type); + + // Determine the 'IEventBus' that this eventType should be registered to. + IEventBus eventBus; + if (IModBusEvent.class.isAssignableFrom(type.eventType)) { + eventBus = FMLJavaModLoadingContext.get().getModEventBus(); + } else { + eventBus = MinecraftForge.EVENT_BUS; + } + + if (IGenericEvent.class.isAssignableFrom(type.eventType)) { + addGenericListener(eventBus, type.genericType, subscribeEvent, type.eventType, invoker); + } else { + addListener(eventBus, subscribeEvent, type.eventType, invoker); + } + + this.listeners.add(new ListenerRegistration(invoker, eventBus, target)); + } + } + + /** + * Unregister previously registered listeners on the target object. + * + * @param target the target listener + */ + public void unregister(Object target) { + this.listeners.removeIf(listener -> { + if (listener.target == target) { + listener.close(); + return true; + } else { + return false; + } + }); + } + + /** + * Unregister all listeners created through this interface. + */ + public void unregisterAll() { + for (ListenerRegistration listener : this.listeners) { + listener.close(); + } + this.listeners.clear(); + } + + /** + * A listener registration. + */ + private static final class ListenerRegistration implements AutoCloseable { + /** The lambda invoker function */ + private final Consumer invoker; + /** The event bus that the invoker was registered to */ + private final IEventBus eventBus; + /** The target listener class */ + private final Object target; + + private ListenerRegistration(Consumer invoker, IEventBus eventBus, Object target) { + this.invoker = invoker; + this.eventBus = eventBus; + this.target = target; + } + + @Override + public void close() { + this.eventBus.unregister(this.invoker); + } + } + + private static Consumer createInvokerFunction(Method method, Object target, EventType type) { + // Use the 'LambdaMetafactory' to generate a consumer which can be passed directly to an 'IEventBus' + // when registering a listener, this reduces the overhead involved when reflectively invoking methods. + try { + MethodHandle methodHandle = LOOKUP.unreflect(method); + CallSite callSite = LambdaMetafactory.metafactory( + LOOKUP, + "accept", + MethodType.methodType(Consumer.class, target.getClass()), + MethodType.methodType(void.class, Object.class), + methodHandle, + MethodType.methodType(void.class, type.eventType) + ); + + return (Consumer) callSite.getTarget().bindTo(target).invokeExact(); + } catch (Throwable t) { + throw new RuntimeException("Error whilst registering " + method, t); + } + } + + public static EventType determineListenerType(Method method) { + // Get the parameter types, this includes generic information which is required for GenericEvent + Type[] parameterTypes = method.getGenericParameterTypes(); + if (parameterTypes.length != 1) { + throw new IllegalArgumentException("" + + "Method " + method + " has @SubscribeEvent annotation. " + + "It has " + parameterTypes.length + " arguments, " + + "but event handler methods require a single argument only." + ); + } + + Type parameterType = parameterTypes[0]; + Class eventType; + Class genericType; + + if (parameterType instanceof Class) { // Non-generic event + eventType = (Class) parameterType; + genericType = null; + } else if (parameterType instanceof ParameterizedType) { // Generic event + ParameterizedType parameterizedType = (ParameterizedType) parameterType; + + // Get the event class + Type rawType = parameterizedType.getRawType(); + if (rawType instanceof Class) { + eventType = (Class) rawType; + } else { + throw new UnsupportedOperationException("Raw Type " + rawType.getClass() + " is not supported"); + } + + // Find the type of 'T' in 'GenericEvent' + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + if (typeArguments.length != 1) { + throw new IllegalArgumentException("" + + "Method " + method + " has @SubscribeEvent annotation. " + + "It has a " + eventType + " argument, " + + "but generic events require a single type argument only." + ); + } + + // Get the generic class + Type typeArgument = typeArguments[0]; + if (typeArgument instanceof Class) { + genericType = (Class) typeArgument; + } else { + throw new UnsupportedOperationException("Type Argument " + typeArgument.getClass() + " is not supported"); + } + } else { + throw new UnsupportedOperationException("Parameter Type " + parameterType.getClass() + " is not supported"); + } + + // Ensure 'genericType' is set if 'eventType' is a generic event + if (GenericEvent.class.isAssignableFrom(eventType) && genericType == null) { + throw new IllegalArgumentException("" + + "Method " + method + " has @SubscribeEvent annotation, " + + "but the generic argument type cannot be determined for " + + "for the GenericEvent subtype: " + eventType + ); + } + + // Ensure 'eventType' is a subclass of event + if (!Event.class.isAssignableFrom(eventType)) { + throw new IllegalArgumentException("" + + "Method " + method + " has @SubscribeEvent annotation, " + + "but takes an argument that is not an Event subtype: " + eventType + ); + } + + return new EventType(eventType, genericType); + } + + private static final class EventType { + private final Class eventType; + private final Class genericType; + + private EventType(Class eventType, Class genericType) { + this.eventType = eventType; + this.genericType = genericType; + } + } + + /** + * Handles casting generics for {@link IEventBus#addGenericListener}. + */ + @SuppressWarnings("unchecked") + private static , F> void addGenericListener(IEventBus eventBus, Class genericClassFilter, SubscribeEvent annotation, Class eventType, Consumer consumer) { + eventBus.addGenericListener((Class) genericClassFilter, annotation.priority(), annotation.receiveCanceled(), (Class) eventType, (Consumer) consumer); + } + + /** + * Handles casting generics for {@link IEventBus#addListener}. + */ + @SuppressWarnings("unchecked") + private static void addListener(IEventBus eventBus, SubscribeEvent annotation, Class eventType, Consumer consumer) { + eventBus.addListener(annotation.priority(), annotation.receiveCanceled(), (Class) eventType, (Consumer) consumer); + } + +} diff --git a/forge/src/main/resources/luckperms.conf b/forge/src/main/resources/luckperms.conf new file mode 100644 index 000000000..0093326c8 --- /dev/null +++ b/forge/src/main/resources/luckperms.conf @@ -0,0 +1,640 @@ +#################################################################################################### +# +----------------------------------------------------------------------------------------------+ # +# | __ __ ___ __ __ | # +# | | | | / ` |__/ |__) |__ |__) |\/| /__` | # +# | |___ \__/ \__, | \ | |___ | \ | | .__/ | # +# | | # +# | https://luckperms.net | # +# | | # +# | WIKI: https://luckperms.net/wiki | # +# | DISCORD: https://discord.gg/luckperms | # +# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | # +# | | # +# | Each option in this file is documented and explained here: | # +# | ==> https://luckperms.net/wiki/Configuration | # +# | | # +# | New options are not added to this file automatically. Default values are used if an | # +# | option cannot be found. The latest config versions can be obtained at the link above. | # +# +----------------------------------------------------------------------------------------------+ # +#################################################################################################### + +# +----------------------------------------------------------------------------------------------+ # +# | | # +# | ESSENTIAL SETTINGS | # +# | | # +# | Important settings that control how LuckPerms functions. | # +# | | # +# +----------------------------------------------------------------------------------------------+ # + +# The name of the server, used for server specific permissions. +# +# - When set to "global" this setting is effectively ignored. +# - In all other cases, the value here is added to all players in a "server" context. +# - See: https://luckperms.net/wiki/Context +server = "global" + +# If the servers own UUID cache/lookup facility should be used when there is no record for a player +# already in LuckPerms. +# +# - When this is set to 'false', commands using a player's username will not work unless the player +# has joined since LuckPerms was first installed. +# - To get around this, you can use a player's uuid directly in the command, or enable this option. +# - When this is set to 'true', the server facility is used. This may use a number of methods, +# including checking the servers local cache, or making a request to the Mojang API. +use-server-uuid-cache = false + + + + +# +----------------------------------------------------------------------------------------------+ # +# | | # +# | STORAGE SETTINGS | # +# | | # +# | Controls which storage method LuckPerms will use to store data. | # +# | | # +# +----------------------------------------------------------------------------------------------+ # + +# How the plugin should store data +# +# - The various options are explained in more detail on the wiki: +# https://luckperms.net/wiki/Storage-types +# +# - Possible options: +# +# | Remote databases - require connection information to be configured below +# |=> MySQL +# |=> MariaDB (preferred over MySQL) +# |=> PostgreSQL +# |=> MongoDB +# +# | Flatfile/local database - don't require any extra configuration +# |=> H2 (preferred over SQLite) +# |=> SQLite +# +# | Readable & editable text files - don't require any extra configuration +# |=> YAML (.yml files) +# |=> JSON (.json files) +# |=> HOCON (.conf files) +# |=> TOML (.toml files) +# | +# | By default, user, group and track data is separated into different files. Data can be combined +# | and all stored in the same file by switching to a combined storage variant. +# | Just add '-combined' to the end of the storage-method, e.g. 'yaml-combined' +# +# - A H2 database is the default option. +# - If you want to edit data manually in "traditional" storage files, we suggest using YAML. +storage-method = "h2" + +# The following block defines the settings for remote database storage methods. +# +# - You don't need to touch any of the settings here if you're using a local storage method! +# - The connection detail options are shared between all remote storage types. +data { + + # Define the address and port for the database. + # - The standard DB engine port is used by default + # (MySQL = 3306, PostgreSQL = 5432, MongoDB = 27017) + # - Specify as "host:port" if differs + address = "localhost" + + # The name of the database to store LuckPerms data in. + # - This must be created already. Don't worry about this setting if you're using MongoDB. + database = "minecraft" + + # Credentials for the database. + username = "root" + password = "" + + # These settings apply to the MySQL connection pool. + # - The default values will be suitable for the majority of users. + # - Do not change these settings unless you know what you're doing! + pool-settings { + + # Sets the maximum size of the MySQL connection pool. + # - Basically this value will determine the maximum number of actual + # connections to the database backend. + # - More information about determining the size of connection pools can be found here: + # https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing + maximum-pool-size = 10 + + # Sets the minimum number of idle connections that the pool will try to maintain. + # - For maximum performance and responsiveness to spike demands, it is recommended to not set + # this value and instead allow the pool to act as a fixed size connection pool. + # (set this value to the same as 'maximum-pool-size') + minimum-idle = 10 + + # This setting controls the maximum lifetime of a connection in the pool in milliseconds. + # - The value should be at least 30 seconds less than any database or infrastructure imposed + # connection time limit. + maximum-lifetime = 1800000 # 30 minutes + + # This setting controls how frequently the pool will 'ping' a connection in order to prevent it + # from being timed out by the database or network infrastructure, measured in milliseconds. + # - The value should be less than maximum-lifetime and greater than 30000 (30 seconds). + # - Setting the value to zero will disable the keepalive functionality. + keepalive-time = 0 + + # This setting controls the maximum number of milliseconds that the plugin will wait for a + # connection from the pool, before timing out. + connection-timeout = 5000 # 5 seconds + + # This setting allows you to define extra properties for connections. + # + # By default, the following options are set to enable utf8 encoding. (you may need to remove + # these if you are using PostgreSQL) + # useUnicode = true + # characterEncoding = "utf8" + # + # You can also use this section to disable SSL connections, by uncommenting the 'useSSL' and + # 'verifyServerCertificate' options below. + properties { + useUnicode = true + characterEncoding = "utf8" + #useSSL: false + #verifyServerCertificate: false + } + } + + # The prefix for all LuckPerms SQL tables. + # + # - This only applies for remote SQL storage types (MySQL, MariaDB, etc). + # - Change this if you want to use different tables for different servers. + table-prefix = "luckperms_" + + # The prefix to use for all LuckPerms MongoDB collections. + # + # - This only applies for the MongoDB storage type. + # - Change this if you want to use different collections for different servers. The default is no + # prefix. + mongodb-collection-prefix = "" + + # The connection string URI to use to connect to the MongoDB instance. + # + # - When configured, this setting will override anything defined in the address, database, + # username or password fields above. + # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it + # below. + # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/ + mongodb-connection-uri = "" +} + +# Define settings for a "split" storage setup. +# +# - This allows you to define a storage method for each type of data. +# - The connection options above still have to be correct for each type here. +split-storage { + # Don't touch this if you don't want to use split storage! + enabled = false + methods { + # These options don't need to be modified if split storage isn't enabled. + user = "h2" + group = "h2" + track = "h2" + uuid = "h2" + log = "h2" + } +} + + + + +# +----------------------------------------------------------------------------------------------+ # +# | | # +# | UPDATE PROPAGATION & MESSAGING SERVICE | # +# | | # +# | Controls the ways in which LuckPerms will sync data & notify other servers of changes. | # +# | These options are documented on greater detail on the wiki under "Instant Updates". | # +# | | # +# +----------------------------------------------------------------------------------------------+ # + +# This option controls how frequently LuckPerms will perform a sync task. +# +# - A sync task will refresh all data from the storage, and ensure that the most up-to-date data is +# being used by the plugin. +# - This is disabled by default, as most users will not need it. However, if you're using a remote +# storage type without a messaging service setup, you may wish to set this to something like 3. +# - Set to -1 to disable the task completely. +sync-minutes = -1 + +# If the file watcher should be enabled. +# +# - When using a file-based storage type, LuckPerms can monitor the data files for changes, and +# automatically update when changes are detected. +# - If you don't want this feature to be active, set this option to false. +watch-files = true + +# Define which messaging service should be used by the plugin. +# +# - If enabled and configured, LuckPerms will use the messaging service to inform other connected +# servers of changes. +# - Use the command "/lp networksync" to manually push changes. +# - Data is NOT stored using this service. It is only used as a messaging platform. +# +# - If you decide to enable this feature, you should set "sync-minutes" to -1, as there is no need +# for LuckPerms to poll the database for changes. +# +# - Possible options: +# => sql Uses the SQL database to form a queue system for communication. Will only work when +# 'storage-method' is set to MySQL or MariaDB. This is chosen by default if the +# option is set to 'auto' and SQL storage is in use. Set to 'notsql' to disable this. +# => pluginmsg Uses the plugin messaging channels to communicate with the proxy. +# LuckPerms must be installed on your proxy & all connected servers backend servers. +# Won't work if you have more than one proxy. +# => redis Uses Redis pub-sub to push changes. Your server connection info must be configured +# below. +# => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be +# configured below. +# => custom Uses a messaging service provided using the LuckPerms API. +# => auto Attempts to automatically setup a messaging service using redis or sql. +messaging-service = "auto" + +# If LuckPerms should automatically push updates after a change has been made with a command. +auto-push-updates = true + +# If LuckPerms should push logging entries to connected servers via the messaging service. +push-log-entries = true + +# If LuckPerms should broadcast received logging entries to players on this platform. +# +# - If you have LuckPerms installed on your backend servers as well as a BungeeCord proxy, you +# should set this option to false on either your backends or your proxies, to avoid players being +# messaged twice about log entries. +broadcast-received-log-entries = true + +# Settings for Redis. +# Port 6379 is used by default; set address to "host:port" if differs +redis { + enabled = false + address = "localhost" + username = "" + password = "" +} + +# Settings for RabbitMQ. +# Port 5672 is used by default; set address to "host:port" if differs +rabbitmq { + enabled = false + address = "localhost" + vhost = "/" + username = "guest" + password = "guest" +} + + + + +# +----------------------------------------------------------------------------------------------+ # +# | | # +# | CUSTOMIZATION SETTINGS | # +# | | # +# | Settings that allow admins to customize the way LuckPerms operates. | # +# | | # +# +----------------------------------------------------------------------------------------------+ # + +# Controls how temporary permissions/parents/meta should be accumulated. +# +# - The default behaviour is "deny". +# - This behaviour can also be specified when the command is executed. See the command usage +# documentation for more info. +# +# - Possible options: +# => accumulate durations will be added to the existing expiry time +# => replace durations will be replaced if the new duration is later than the current +# expiration +# => deny the command will just fail if you try to add another node with the same expiry +temporary-add-behaviour = "deny" + +# Controls how LuckPerms will determine a users "primary" group. +# +# - The meaning and influence of "primary groups" are explained in detail on the wiki. +# - The preferred approach is to let LuckPerms automatically determine a users primary group +# based on the relative weight of their parent groups. +# +# - Possible options: +# => stored use the value stored against the users record in the file/database +# => parents-by-weight just use the users most highly weighted parent +# => all-parents-by-weight same as above, but calculates based upon all parents inherited from +# both directly and indirectly +primary-group-calculation = "parents-by-weight" + +# If the plugin should check for "extra" permissions with users run LP commands. +# +# - These extra permissions allow finer control over what users can do with each command, and who +# they have access to edit. +# - The nature of the checks are documented on the wiki under "Argument based command permissions". +# - Argument based permissions are *not* static, unlike the 'base' permissions, and will depend upon +# the arguments given within the command. +argument-based-command-permissions = false + +# If the plugin should check whether senders are a member of a given group before they're able to +# edit the groups data or add/remove other users to/from it. +# Note: these limitations do not apply to the web editor! +require-sender-group-membership-to-modify = false + +# If the plugin should send log notifications to users whenever permissions are modified. +# +# - Notifications are only sent to those with the appropriate permission to receive them +# - They can also be temporarily enabled/disabled on a per-user basis using +# '/lp log notify ' +log-notify = true + +# Defines a list of log entries which should not be sent as notifications to users. +# +# - Each entry in the list is a RegEx expression which is matched against the log entry description. +log-notify-filtered-descriptions = [ +# "parent add example" +] + +# If LuckPerms should automatically install translation bundles and periodically update them. +auto-install-translations = true + +# Defines the options for prefix and suffix stacking. +# +# - The feature allows you to display multiple prefixes or suffixes alongside a players username in +# chat. +# - It is explained and documented in more detail on the wiki under "Prefix & Suffix Stacking". +# +# - The options are divided into separate sections for prefixes and suffixes. +# - The 'duplicates' setting refers to how duplicate elements are handled. Can be 'retain-all', +# 'first-only' or 'last-only'. +# - The value of 'start-spacer' is included at the start of the resultant prefix/suffix. +# - The value of 'end-spacer' is included at the end of the resultant prefix/suffix. +# - The value of 'middle-spacer' is included between each element in the resultant prefix/suffix. +# +# - Possible format options: +# => highest Selects the value with the highest weight, from all values +# held by or inherited by the player. +# +# => lowest Same as above, except takes the one with the lowest weight. +# +# => highest_own Selects the value with the highest weight, but will not +# accept any inherited values. +# +# => lowest_own Same as above, except takes the value with the lowest weight. +# +# => highest_inherited Selects the value with the highest weight, but will only +# accept inherited values. +# +# => lowest_inherited Same as above, except takes the value with the lowest weight. +# +# => highest_on_track_ Selects the value with the highest weight, but only if the +# value was inherited from a group on the given track. +# +# => lowest_on_track_ Same as above, except takes the value with the lowest weight. +# +# => highest_not_on_track_ Selects the value with the highest weight, but only if the +# value was inherited from a group not on the given track. +# +# => lowest_not_on_track_ Same as above, except takes the value with the lowest weight. +# +# => highest_from_group_ Selects the value with the highest weight, but only if the +# value was inherited from the given group. +# +# => lowest_from_group_ Same as above, except takes the value with the lowest weight. +# +# => highest_not_from_group_ Selects the value with the highest weight, but only if the +# value was not inherited from the given group. +# +# => lowest_not_from_group_ Same as above, except takes the value with the lowest weight. +meta-formatting { + prefix { + format = [ + "highest" + ] + duplicates = "first-only" + start-spacer = "" + middle-spacer = " " + end-spacer = "" + } + suffix { + format = [ + "highest" + ] + duplicates = "first-only" + start-spacer = "" + middle-spacer = " " + end-spacer = "" + } +} + + + + +# +----------------------------------------------------------------------------------------------+ # +# | | # +# | PERMISSION CALCULATION AND INHERITANCE | # +# | | # +# | Modify the way permission checks, meta lookups and inheritance resolutions are handled. | # +# | | # +# +----------------------------------------------------------------------------------------------+ # + +# The algorithm LuckPerms should use when traversing the "inheritance tree". +# +# - Possible options: +# => breadth-first See: https://en.wikipedia.org/wiki/Breadth-first_search +# => depth-first-pre-order See: https://en.wikipedia.org/wiki/Depth-first_search +# => depth-first-post-order See: https://en.wikipedia.org/wiki/Depth-first_search +inheritance-traversal-algorithm = "depth-first-pre-order" + +# If a final sort according to "inheritance rules" should be performed after the traversal algorithm +# has resolved the inheritance tree. +# +# "Inheritance rules" refers to things such as group weightings, primary group status, and the +# natural contextual ordering of the group nodes. +# +# Setting this to 'true' will allow for the inheritance rules to take priority over the structure of +# the inheritance tree. +# +# Effectively when this setting is 'true': the tree is flattened, and rules applied afterwards, +# and when this setting is 'false':, the rules are just applied during each step of the traversal. +post-traversal-inheritance-sort = false + +# Defines the mode used to determine whether a set of contexts are satisfied. +# +# - Possible options: +# => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the +# key-value entries per key in A are also in B. +# => all-values-per-key Set A will be satisfied by another set B, if all key-value +# entries in A are also in B. +context-satisfy-mode = "at-least-one-value-per-key" + +# LuckPerms has a number of built-in contexts. These can be disabled by adding the context key to +# the list below. +disabled-contexts = [ +# "world" +] + +# +----------------------------------------------------------------------------------------------+ # +# | Permission resolution settings | # +# +----------------------------------------------------------------------------------------------+ # + +# If users on this server should have their global permissions applied. +# When set to false, only server specific permissions will apply for users on this server +include-global = true + +# If users on this server should have their global world permissions applied. +# When set to false, only world specific permissions will apply for users on this server +include-global-world = true + +# If users on this server should have global (non-server specific) groups applied +apply-global-groups = true + +# If users on this server should have global (non-world specific) groups applied +apply-global-world-groups = true + +# +----------------------------------------------------------------------------------------------+ # +# | Meta lookup settings | # +# +----------------------------------------------------------------------------------------------+ # + +# Defines how meta values should be selected. +# +# - Possible options: +# => inheritance Selects the meta value that was inherited first +# => highest-number Selects the highest numerical meta value +# => lowest-number Selects the lowest numerical meta value +meta-value-selection-default = "inheritance" + +# Defines how meta values should be selected per key. +meta-value-selection { + #max-homes = "highest-number" +} + +# +----------------------------------------------------------------------------------------------+ # +# | Inheritance settings | # +# +----------------------------------------------------------------------------------------------+ # + +# If the plugin should apply wildcard permissions. +# +# - If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered +# permissions matching the wildcard. +apply-wildcards = true + +# If LuckPerms should resolve and apply permissions according to the Sponge style implicit wildcard +# inheritance system. +# +# - That being: If a user has been granted "example", then the player should have also be +# automatically granted "example.function", "example.another", "example.deeper.nesting", +# and so on. +apply-sponge-implicit-wildcards = false + +# If the plugin should parse regex permissions. +# +# - If set to true, LuckPerms will detect regex permissions, marked with "r=" at the start of the +# node, and resolve & apply all registered permissions matching the regex. +apply-regex = true + +# If the plugin should complete and apply shorthand permissions. +# +# - If set to true, LuckPerms will detect and expand shorthand node patterns. +apply-shorthand = true + +# If the owner of an integrated server should bypass permission checks. +# +# - This setting only applies when LuckPerms is active on a single-player world. +# - The owner of an integrated server is the player whose client instance is running the server. +integrated-server-owner-bypasses-checks = true + +# +----------------------------------------------------------------------------------------------+ # +# | Extra settings | # +# +----------------------------------------------------------------------------------------------+ # + +# A list of context calculators which will be skipped when calculating contexts. +# +# - You can disable context calculators by either: +# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator) +# => specifying a sub-section of the Java package used by the calculator (e.g. com.example) +disabled-context-calculators = [] + +# Allows you to set "aliases" for the worlds sent forward for context calculation. +# +# - These aliases are provided in addition to the real world name. Applied recursively. +# - Remove the comment characters for the default aliases to apply. +world-rewrite { + #world_nether = "world" + #world_the_end = "world" +} + +# Define special group weights for this server. +# +# - Group weights can also be applied directly to group data, using the setweight command. +# - This section allows weights to be set on a per-server basis. +group-weight { + #admin = 10 +} + + + +# +----------------------------------------------------------------------------------------------+ # +# | | # +# | FINE TUNING OPTIONS | # +# | | # +# | A number of more niche settings for tweaking and changing behaviour. The section also | # +# | contains toggles for some more specialised features. It is only necessary to make changes to | # +# | these options if you want to fine-tune LuckPerms behaviour. | # +# | | # +# +----------------------------------------------------------------------------------------------+ # + +# +----------------------------------------------------------------------------------------------+ # +# | Server Operator (OP) settings | # +# +----------------------------------------------------------------------------------------------+ # + +# Controls whether server operators should exist at all. +# +# - When set to 'false', all players will be de-opped, and the /op and /deop commands will be +# disabled. Note that vanilla features like the spawn-protection require an operator on the +# server to work. +enable-ops = true + +# Enables or disables a special permission based system in LuckPerms for controlling OP status. +# +# - If set to true, any user with the permission "luckperms.autoop" will automatically be granted +# server operator status. This permission can be inherited, or set on specific servers/worlds, +# temporarily, etc. +# - Additionally, setting this to true will force the "enable-ops" option above to false. All users +# will be de-opped unless they have the permission node, and the op/deop commands will be +# disabled. +# - It is recommended that you use this option instead of assigning a single '*' permission. +# - However, on Forge this setting can be used as a "pseudo" root wildcard, as many mods support +# the operator system over permissions. +auto-op = false + +# +----------------------------------------------------------------------------------------------+ # +# | Miscellaneous (and rarely used) settings | # +# +----------------------------------------------------------------------------------------------+ # + +# If LuckPerms should produce extra logging output when it handles logins. +# +# - Useful if you're having issues with UUID forwarding or data not being loaded. +debug-logins = false + +# If LuckPerms should allow usernames with non alphanumeric characters. +# +# - Note that due to the design of the storage implementation, usernames must still be 16 characters +# or less. +allow-invalid-usernames = false + +# If LuckPerms should not require users to confirm bulkupdate operations. +# +# - When set to true, operations will be executed immediately. +# - This is not recommended, as bulkupdate has the potential to irreversibly delete large amounts of +# data, and is not designed to be executed automatically. +# - If automation is needed, users should prefer using the LuckPerms API. +skip-bulkupdate-confirmation = false + +# If LuckPerms should prevent bulkupdate operations. +# +# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work. +# - When set to false, bulkupdate operations will be allowed via the console. +disable-bulkupdate = false + +# If LuckPerms should allow a users primary group to be removed with the 'parent remove' command. +# +# - When this happens, the plugin will set their primary group back to default. +prevent-primary-group-removal = false + +# If LuckPerms should update the list of commands sent to the client when permissions are changed. +update-client-command-list = true + +# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands. +# See here for more info: https://minecraft.gamepedia.com/Commands#Target_selectors +resolve-command-selectors = false diff --git a/gradle.properties b/gradle.properties index f12c912d6..2668b73c0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,4 @@ # Fabric requires some more ram. -org.gradle.jvmargs=-Xmx1G \ No newline at end of file +org.gradle.jvmargs=-Xmx1G +# ForgeGradle is special. +org.gradle.daemon=false \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 17a0c9c99..06b95aad0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -21,6 +21,9 @@ include ( 'bungee', 'bungee:loader', 'fabric', + 'forge', + 'forge:loader', + 'forge:forge-api', 'nukkit', 'nukkit:loader', 'sponge', From 62a8891b4830d4685d739e82af82733a52d56bd0 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 20 May 2022 20:12:56 +0100 Subject: [PATCH 095/314] Fix buildscript --- forge/loader/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge/loader/build.gradle b/forge/loader/build.gradle index c66d7276b..3eca8f201 100644 --- a/forge/loader/build.gradle +++ b/forge/loader/build.gradle @@ -33,7 +33,7 @@ dependencies { build { dependsOn(":forge:build") - dependsOn(":forge-api:build") + dependsOn(":forge:forge-api:build") } jar { From a3685095203bd6a30ce3342b78b0901015ed340b Mon Sep 17 00:00:00 2001 From: IzzelAliz Date: Tue, 7 Jun 2022 01:39:13 +0800 Subject: [PATCH 096/314] Use Forge default resolver when result is undefined (#3390) --- .../lucko/luckperms/forge/service/ForgePermissionHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java b/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java index 86ed3e20d..47b35852d 100644 --- a/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java +++ b/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java @@ -115,7 +115,9 @@ private static T getPermissionValue(User user, QueryOptions queryOptions, Pe if (node.getType() == PermissionTypes.BOOLEAN) { PermissionCache cache = user.getCachedData().getPermissionData(queryOptions); Tristate value = cache.checkPermission(node.getNodeName(), CheckOrigin.PLATFORM_API_HAS_PERMISSION).result(); - return (T) (Boolean) value.asBoolean(); + if (value != Tristate.UNDEFINED) { + return (T) (Boolean) value.asBoolean(); + } } if (node.getType() == PermissionTypes.INTEGER) { From 7824566ce545c9b075eede78f0e280b8a2232085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien?= <43724816+Aurelien30000@users.noreply.github.com> Date: Sat, 11 Jun 2022 22:14:14 +0200 Subject: [PATCH 097/314] Fabric 1.19 (#3394) --- fabric/build.gradle | 12 ++++++------ .../luckperms/fabric/FabricCommandExecutor.java | 4 ++-- .../fabric/listeners/FabricConnectionListener.java | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fabric/build.gradle b/fabric/build.gradle index 7c6cda7b4..d557231d4 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -2,7 +2,7 @@ import net.fabricmc.loom.task.RemapJarTask plugins { id 'com.github.johnrengelman.shadow' version '7.0.0' - id 'fabric-loom' version '0.9.+' + id 'fabric-loom' version '0.12.+' } archivesBaseName = 'luckperms' @@ -14,19 +14,19 @@ repositories { dependencies { // https://modmuss50.me/fabric.html - minecraft 'com.mojang:minecraft:1.18' - mappings 'net.fabricmc:yarn:1.18+build.1:v2' - modImplementation 'net.fabricmc:fabric-loader:0.12.6' + minecraft 'com.mojang:minecraft:1.19' + mappings 'net.fabricmc:yarn:1.19+build.1:v2' + modImplementation 'net.fabricmc:fabric-loader:0.14.6' Set apiModules = [ 'fabric-api-base', - 'fabric-command-api-v1', + 'fabric-command-api-v2', 'fabric-lifecycle-events-v1', 'fabric-networking-api-v1' ] apiModules.forEach { - modImplementation(fabricApi.module(it, '0.43.1+1.18')) + modImplementation(fabricApi.module(it, '0.55.2+1.19')) } include(modImplementation('me.lucko:fabric-permissions-api:0.1-SNAPSHOT')) diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java index f7b42aa97..39e2bffdc 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/FabricCommandExecutor.java @@ -31,7 +31,7 @@ import com.mojang.brigadier.tree.LiteralCommandNode; import me.lucko.luckperms.common.command.BrigadierCommandExecutor; import me.lucko.luckperms.common.sender.Sender; -import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.command.argument.EntityArgumentType; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; @@ -53,7 +53,7 @@ public FabricCommandExecutor(LPFabricPlugin plugin) { } public void register() { - CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { for (String alias : COMMAND_ALIASES) { LiteralCommandNode cmd = literal(alias) .executes(this) diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricConnectionListener.java b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricConnectionListener.java index c93ece113..d89673900 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricConnectionListener.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricConnectionListener.java @@ -42,11 +42,11 @@ import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking.LoginSynchronizer; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.kyori.adventure.text.Component; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerLoginNetworkHandler; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.dynamic.DynamicSerializableUuid; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -70,7 +70,7 @@ private void onPreLogin(ServerLoginNetworkHandler netHandler, MinecraftServer se // Get their profile from the net handler - it should have been initialised by now. GameProfile profile = ((ServerLoginNetworkHandlerAccessor) netHandler).getGameProfile(); - UUID uniqueId = PlayerEntity.getUuidFromProfile(profile); + UUID uniqueId = DynamicSerializableUuid.getUuidFromProfile(profile); String username = profile.getName(); if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { From 79a08610e0292f27b973d581d48d6afe221c6807 Mon Sep 17 00:00:00 2001 From: lucko Date: Sun, 12 Jun 2022 10:55:51 +0100 Subject: [PATCH 098/314] Increase memory allocation for Gradle --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2668b73c0..ab51df5b1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ # Fabric requires some more ram. -org.gradle.jvmargs=-Xmx1G +org.gradle.jvmargs=-Xmx2G # ForgeGradle is special. -org.gradle.daemon=false \ No newline at end of file +org.gradle.daemon=false From 9b2bbb3e5353825633978425bad95c4d44a6f424 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 12 Jun 2022 17:48:29 +0100 Subject: [PATCH 099/314] Bump adventure version --- bukkit/build.gradle | 2 +- bungee/build.gradle | 2 +- common/build.gradle | 8 ++++---- .../common/dependencies/Dependency.java | 16 ++++++++-------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bukkit/build.gradle b/bukkit/build.gradle index fcdbe4ef4..cabcea382 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -11,7 +11,7 @@ dependencies { compileOnly project(':common:loader-utils') compileOnly 'com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT' - compileOnly('me.lucko:adventure-platform-bukkit:4.9.4') { + compileOnly('net.kyori:adventure-platform-bukkit:4.1.0') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') diff --git a/bungee/build.gradle b/bungee/build.gradle index 33ea496e8..06a21987a 100644 --- a/bungee/build.gradle +++ b/bungee/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly project(':common:loader-utils') compileOnly 'net.md-5:bungeecord-api:1.16-R0.4' - compileOnly('me.lucko:adventure-platform-bungeecord:4.9.4') { + compileOnly('net.kyori:adventure-platform-bungeecord:4.1.0') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') diff --git a/common/build.gradle b/common/build.gradle index 374a0ade5..6e4a53c51 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -21,24 +21,24 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.30' compileOnly 'org.apache.logging.log4j:log4j-api:2.14.0' - api('net.kyori:adventure-api:4.9.3') { + api('net.kyori:adventure-api:4.11.0') { exclude(module: 'adventure-bom') exclude(module: 'checker-qual') exclude(module: 'annotations') } - api('net.kyori:adventure-text-serializer-gson:4.9.3') { + api('net.kyori:adventure-text-serializer-gson:4.11.0') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') exclude(module: 'gson') } - api('net.kyori:adventure-text-serializer-legacy:4.9.3') { + api('net.kyori:adventure-text-serializer-legacy:4.11.0') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } - api('net.kyori:adventure-text-serializer-plain:4.9.3') { + api('net.kyori:adventure-text-serializer-plain:4.11.0') { exclude(module: 'adventure-bom') exclude(module: 'adventure-api') } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index ee4fe72cd..85c24ca16 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -64,29 +64,29 @@ public enum Dependency { ADVENTURE( "me{}lucko", "adventure-api", - "4.9.4", - "bh1cB0ZdzxstqRfVowKbgAZM16T/CtJ7oIJCaknbGeM=", + "4.11.0", + "7xrFaBbsTiQPZKBDzvDnTd8XIgOsHTy9qQICm3342GU=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM( "me{}lucko", "adventure-platform-api", - "4.9.4", - "CZVyxNOi7ln7dgldfDs1m1M6d9w/bKG8Rp27qfAuTJw=", + "4.11.0", + "rFUSnKY6xEGCjgxqSl6/oJw2deCA/x834HlcCQEI1K4=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUKKIT( "me{}lucko", "adventure-platform-bukkit", - "4.9.4", - "JkA3GGkf3pADA3PACYfdVlAglstgzz+ajAxElMXXn6I=", + "4.11.0", + "usbxLJmMp+fb9cnnRNlHnOVSgd5NZfColiVlsjmMulo=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUNGEECORD( "me{}lucko", "adventure-platform-bungeecord", - "4.9.4", - "a/p8/eIjbQnNlN5cJ/yIUjttZyJ/GRXgcsg8rhY7SwI=", + "4.11.0", + "+WUdRdZ6qkacw3ha/R3ayLx46soMywGe70Zmnw4yha8=", Relocation.of("adventure", "net{}kyori{}adventure") ), EVENT( From 016f35a6f02d23634b7d720f1fc9eff40dba2694 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 12 Jun 2022 20:54:33 +0100 Subject: [PATCH 100/314] Update Bukkit dependencies for full 1.19 support --- bukkit/build.gradle | 2 +- .../bukkit/brigadier/LuckPermsBrigadier.java | 4 ++-- .../luckperms/common/dependencies/Dependency.java | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bukkit/build.gradle b/bukkit/build.gradle index cabcea382..2e1a143e9 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -16,7 +16,7 @@ dependencies { exclude(module: 'adventure-api') exclude(module: 'adventure-nbt') } - compileOnly 'me.lucko:commodore:1.13' + compileOnly 'me.lucko:commodore:2.0' compileOnly('net.milkbowl.vault:VaultAPI:1.7') { exclude(module: 'bukkit') } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/brigadier/LuckPermsBrigadier.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/brigadier/LuckPermsBrigadier.java index 8c8eaf348..cd9bda179 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/brigadier/LuckPermsBrigadier.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/brigadier/LuckPermsBrigadier.java @@ -29,7 +29,7 @@ import me.lucko.commodore.Commodore; import me.lucko.commodore.CommodoreProvider; -import me.lucko.commodore.file.CommodoreFileFormat; +import me.lucko.commodore.file.CommodoreFileReader; import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.common.sender.Sender; @@ -50,7 +50,7 @@ public static void register(LPBukkitPlugin plugin, Command pluginCommand) throws throw new Exception("Brigadier command data missing from jar"); } - LiteralCommandNode commandNode = CommodoreFileFormat.parse(is); + LiteralCommandNode commandNode = CommodoreFileReader.INSTANCE.parse(is); commodore.register(pluginCommand, commandNode, player -> { Sender playerAsSender = plugin.getSenderFactory().wrap(player); return plugin.getCommandManager().hasPermissionForAny(playerAsSender); diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index 85c24ca16..91ce28132 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -71,21 +71,21 @@ public enum Dependency { ADVENTURE_PLATFORM( "me{}lucko", "adventure-platform-api", - "4.11.0", - "rFUSnKY6xEGCjgxqSl6/oJw2deCA/x834HlcCQEI1K4=", + "4.11.1", + "zCnxNgosme++TsheFaL+YHdtRIrp+oJhUiI8awsKgfQ=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUKKIT( "me{}lucko", "adventure-platform-bukkit", - "4.11.0", - "usbxLJmMp+fb9cnnRNlHnOVSgd5NZfColiVlsjmMulo=", + "4.11.1", + "+xpPjOz1YkhB2Dq8JDNPcKOm4IUijolg10a2x+ISpBc=", Relocation.of("adventure", "net{}kyori{}adventure") ), ADVENTURE_PLATFORM_BUNGEECORD( "me{}lucko", "adventure-platform-bungeecord", - "4.11.0", + "4.11.1", "+WUdRdZ6qkacw3ha/R3ayLx46soMywGe70Zmnw4yha8=", Relocation.of("adventure", "net{}kyori{}adventure") ), @@ -128,8 +128,8 @@ public enum Dependency { COMMODORE( "me{}lucko", "commodore", - "1.13", - "A4M1Im54vrYbcdSpteQvI6ppNPYWL+VpfBVjsdteY+M=", + "2.0", + "5pPvU2MFRK4doXDOG8UKTp1y7D7uuTfiPglTosUh4zk=", Relocation.of("commodore", "me{}lucko{}commodore") ), COMMODORE_FILE( From bf93077474a3b5c58ca07f84fc3d29cd77810db9 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 17 Jul 2022 11:53:52 +0100 Subject: [PATCH 101/314] MetaCache refactoring --- .../api/cacheddata/CachedMetaData.java | 25 ++++ .../cacheddata/result/IntegerResult.java | 125 ++++++++++++++++++ .../cacheddata/type/MetaAccumulator.java | 17 ++- .../common/cacheddata/type/MetaCache.java | 83 ++++++------ .../cacheddata/type/MonitoredMetaCache.java | 19 ++- .../lucko/luckperms/common/model/Group.java | 7 +- .../common/model/PermissionHolder.java | 15 ++- .../luckperms/common/model/WeightCache.java | 21 ++- .../forge/service/ForgePermissionHandler.java | 24 +++- 9 files changed, 252 insertions(+), 84 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/cacheddata/result/IntegerResult.java diff --git a/api/src/main/java/net/luckperms/api/cacheddata/CachedMetaData.java b/api/src/main/java/net/luckperms/api/cacheddata/CachedMetaData.java index 467d3c48c..15220016f 100644 --- a/api/src/main/java/net/luckperms/api/cacheddata/CachedMetaData.java +++ b/api/src/main/java/net/luckperms/api/cacheddata/CachedMetaData.java @@ -29,6 +29,7 @@ import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.node.types.PrefixNode; import net.luckperms.api.node.types.SuffixNode; +import net.luckperms.api.node.types.WeightNode; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -177,6 +178,30 @@ public interface CachedMetaData extends CachedData { return querySuffix().result(); } + /** + * Query for a weight. + * + *

    This method will always return a {@link Result}, and the + * {@link Result#result() inner result} {@link Integer} will never be null. + * A value of {@code 0} is equivalent to null.

    + * + * @return a result containing the weight + * @since 5.5 + */ + @NonNull Result queryWeight(); + + /** + * Gets the weight. + * + *

    If the there is no defined weight, {@code 0} is returned.

    + * + * @return the weight + * @since 5.5 + */ + default int getWeight() { + return queryWeight().result(); + } + /** * Gets a map of all accumulated {@link MetaNode meta}. * diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/IntegerResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/IntegerResult.java new file mode 100644 index 000000000..31e89226b --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/IntegerResult.java @@ -0,0 +1,125 @@ +/* + * 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.cacheddata.result; + +import net.luckperms.api.cacheddata.Result; +import net.luckperms.api.node.Node; +import net.luckperms.api.node.types.WeightNode; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Represents the result of an integer meta lookup + * + * @param the node type + */ +public final class IntegerResult implements Result { + + /** The result */ + private final int result; + /** The node that caused the result */ + private final N node; + /** A reference to another result that this one overrides */ + private IntegerResult overriddenResult; + + public IntegerResult(int result, N node, IntegerResult overriddenResult) { + this.result = result; + this.node = node; + this.overriddenResult = overriddenResult; + } + + @Override + @Deprecated // use intResult() + public @NonNull Integer result() { + return this.result; + } + + public int intResult() { + return this.result; + } + + public StringResult asStringResult() { + if (isNull()) { + return StringResult.nullResult(); + } else { + StringResult result = StringResult.of(Integer.toString(this.result), this.node); + if (this.overriddenResult != null) { + result.setOverriddenResult(this.overriddenResult.asStringResult()); + } + return result; + } + } + + @Override + public @Nullable N node() { + return this.node; + } + + public @Nullable IntegerResult overriddenResult() { + return this.overriddenResult; + } + + public void setOverriddenResult(IntegerResult overriddenResult) { + this.overriddenResult = overriddenResult; + } + + public boolean isNull() { + return this == NULL_RESULT; + } + + public IntegerResult copy() { + return new IntegerResult<>(this.result, this.node, this.overriddenResult); + } + + @Override + public String toString() { + return "IntegerResult(" + + "result=" + this.result + ", " + + "node=" + this.node + ", " + + "overriddenResult=" + this.overriddenResult + ')'; + } + + private static final IntegerResult NULL_RESULT = new IntegerResult<>(0, null, null); + + @SuppressWarnings("unchecked") + public static IntegerResult nullResult() { + return (IntegerResult) NULL_RESULT; + } + + public static IntegerResult of(int result) { + return new IntegerResult<>(result, null, null); + } + + public static IntegerResult of(int result, N node) { + return new IntegerResult<>(result, node, null); + } + + public static IntegerResult of(WeightNode node) { + return new IntegerResult<>(node.getWeight(), node, null); + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java index ce2255a61..98b29f49b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaAccumulator.java @@ -28,6 +28,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import me.lucko.luckperms.common.cacheddata.result.IntegerResult; import me.lucko.luckperms.common.cacheddata.result.StringResult; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.node.types.Weight; @@ -40,6 +41,7 @@ import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.node.types.PrefixNode; import net.luckperms.api.node.types.SuffixNode; +import net.luckperms.api.node.types.WeightNode; import java.util.Comparator; import java.util.HashSet; @@ -80,7 +82,7 @@ private enum State { private final ListMultimap> meta; private final SortedMap> prefixes; private final SortedMap> suffixes; - private int weight = 0; + private IntegerResult weight; private String primaryGroup; private Set seenNodeKeys = new HashSet<>(); @@ -96,6 +98,7 @@ public MetaAccumulator(MetaStackDefinition prefixDefinition, MetaStackDefinition this.meta = ArrayListMultimap.create(); this.prefixes = new TreeMap<>(Comparator.reverseOrder()); this.suffixes = new TreeMap<>(Comparator.reverseOrder()); + this.weight = IntegerResult.nullResult(); this.prefixDefinition = prefixDefinition; this.suffixDefinition = suffixDefinition; this.prefixAccumulator = new MetaStackAccumulator<>(this.prefixDefinition, ChatMetaType.PREFIX); @@ -120,8 +123,8 @@ public void complete() { } // perform final changes - if (!this.meta.containsKey(Weight.NODE_KEY) && this.weight != 0) { - this.meta.put(Weight.NODE_KEY, StringResult.of(String.valueOf(this.weight))); + if (!this.meta.containsKey(Weight.NODE_KEY) && !this.weight.isNull()) { + this.meta.put(Weight.NODE_KEY, StringResult.of(String.valueOf(this.weight.intResult()))); } if (this.primaryGroup != null && !this.meta.containsKey("primarygroup")) { this.meta.put("primarygroup", StringResult.of(this.primaryGroup)); @@ -164,9 +167,11 @@ public void accumulateNode(Node n) { } } - public void accumulateWeight(int weight) { + public void accumulateWeight(IntegerResult weight) { ensureState(State.ACCUMULATING); - this.weight = Math.max(this.weight, weight); + if (this.weight.isNull() || weight.intResult() > this.weight.intResult()) { + this.weight = weight; + } } public void setPrimaryGroup(String primaryGroup) { @@ -196,7 +201,7 @@ public SortedMap> getSuffixes() { return this.suffixes; } - public int getWeight() { + public IntegerResult getWeight() { ensureState(State.COMPLETE); return this.weight; } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java index da8f7ce23..a6e8469cd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MetaCache.java @@ -34,8 +34,11 @@ import com.google.common.collect.Multimaps; import me.lucko.luckperms.common.cacheddata.UsageTracked; +import me.lucko.luckperms.common.cacheddata.result.IntegerResult; import me.lucko.luckperms.common.cacheddata.result.StringResult; import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.node.types.Prefix; +import me.lucko.luckperms.common.node.types.Suffix; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.verbose.event.CheckOrigin; @@ -45,6 +48,7 @@ import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.node.types.PrefixNode; import net.luckperms.api.node.types.SuffixNode; +import net.luckperms.api.node.types.WeightNode; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.query.meta.MetaValueSelector; @@ -72,7 +76,7 @@ public class MetaCache extends UsageTracked implements CachedMetaData { private final Map> flattenedMeta; private final SortedMap> prefixes; private final SortedMap> suffixes; - private final int weight; + private final IntegerResult weight; private final String primaryGroup; private final MetaStackDefinition prefixDefinition; private final MetaStackDefinition suffixDefinition; @@ -119,78 +123,63 @@ public MetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, MetaAccumula return this.flattenedMeta.getOrDefault(key.toLowerCase(Locale.ROOT), StringResult.nullResult()); } - @Override - public final @NonNull Result queryMetaValue(@NonNull String key) { - return getMetaValue(key, CheckOrigin.LUCKPERMS_API); - } - - @Override - public final @Nullable String getMetaValue(@NonNull String key) { - return getMetaValue(key, CheckOrigin.LUCKPERMS_API).result(); - } - public @NonNull StringResult getPrefix(CheckOrigin origin) { return this.prefix; } - @Override - public final @NonNull Result queryPrefix() { - return getPrefix(CheckOrigin.LUCKPERMS_API); - } - - @Override - public final @Nullable String getPrefix() { - return getPrefix(CheckOrigin.LUCKPERMS_API).result(); - } - public @NonNull StringResult getSuffix(CheckOrigin origin) { return this.suffix; } - @Override - public final @NonNull Result querySuffix() { - return getSuffix(CheckOrigin.LUCKPERMS_API); + public @NonNull IntegerResult getWeight(CheckOrigin origin) { + return this.weight; } - @Override - public final @Nullable String getSuffix() { - return getSuffix(CheckOrigin.LUCKPERMS_API).result(); + public @NonNull Map>> getMetaResults(CheckOrigin origin) { + return this.meta; } - protected Map>> getMetaResults(CheckOrigin origin) { - return this.meta; + public @Nullable String getPrimaryGroup(CheckOrigin origin) { + return this.primaryGroup; } public final Map> getMeta(CheckOrigin origin) { return Maps.transformValues(getMetaResults(origin), list -> Lists.transform(list, StringResult::result)); } - @Override - public final @NonNull Map> getMeta() { - return getMeta(CheckOrigin.LUCKPERMS_API); + public @Nullable String getMetaOrChatMetaValue(String key, CheckOrigin origin) { + if (key.equals(Prefix.NODE_KEY)) { + return getPrefix(origin).result(); + } else if (key.equals(Suffix.NODE_KEY)) { + return getSuffix(origin).result(); + } else { + return getMetaValue(key, origin).result(); + } } @Override - public @NonNull SortedMap getPrefixes() { - return Maps.transformValues(this.prefixes, StringResult::result); + public final @NonNull Result queryMetaValue(@NonNull String key) { + return getMetaValue(key, CheckOrigin.LUCKPERMS_API); } @Override - public @NonNull SortedMap getSuffixes() { - return Maps.transformValues(this.suffixes, StringResult::result); + public final @NonNull Result queryPrefix() { + return getPrefix(CheckOrigin.LUCKPERMS_API); } - public int getWeight(CheckOrigin origin) { - return this.weight; + @Override + public final @NonNull Result querySuffix() { + return getSuffix(CheckOrigin.LUCKPERMS_API); } - //@Override - not actually exposed in the API atm - public final int getWeight() { + @Override + public @NonNull Result queryWeight() { return getWeight(CheckOrigin.LUCKPERMS_API); } - public @Nullable String getPrimaryGroup(CheckOrigin origin) { - return this.primaryGroup; + @Override + public final @NonNull Map> getMeta() { + return getMeta(CheckOrigin.LUCKPERMS_API); } @Override @@ -198,6 +187,16 @@ public final int getWeight() { return getPrimaryGroup(CheckOrigin.LUCKPERMS_API); } + @Override + public @NonNull SortedMap getPrefixes() { + return Maps.transformValues(this.prefixes, StringResult::result); + } + + @Override + public @NonNull SortedMap getSuffixes() { + return Maps.transformValues(this.suffixes, StringResult::result); + } + @Override public @NonNull MetaStackDefinition getPrefixStackDefinition() { return this.prefixDefinition; diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java index ecd2116c6..7b56a5c99 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/type/MonitoredMetaCache.java @@ -28,6 +28,7 @@ import com.google.common.collect.ForwardingMap; import me.lucko.luckperms.common.cacheddata.CacheMetadata; +import me.lucko.luckperms.common.cacheddata.result.IntegerResult; import me.lucko.luckperms.common.cacheddata.result.StringResult; import me.lucko.luckperms.common.node.types.Prefix; import me.lucko.luckperms.common.node.types.Suffix; @@ -38,6 +39,7 @@ import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.node.types.PrefixNode; import net.luckperms.api.node.types.SuffixNode; +import net.luckperms.api.node.types.WeightNode; import net.luckperms.api.query.QueryOptions; import org.checkerframework.checker.nullness.qual.NonNull; @@ -65,38 +67,35 @@ public MonitoredMetaCache(LuckPermsPlugin plugin, QueryOptions queryOptions, Cac } @Override - @NonNull - public StringResult getMetaValue(String key, CheckOrigin origin) { + public @NonNull StringResult getMetaValue(String key, CheckOrigin origin) { StringResult value = super.getMetaValue(key, origin); this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), key, value); return value; } @Override - @NonNull - public StringResult getPrefix(CheckOrigin origin) { + public @NonNull StringResult getPrefix(CheckOrigin origin) { StringResult value = super.getPrefix(origin); this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Prefix.NODE_KEY, value); return value; } @Override - @NonNull - public StringResult getSuffix(CheckOrigin origin) { + public @NonNull StringResult getSuffix(CheckOrigin origin) { StringResult value = super.getSuffix(origin); this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), Suffix.NODE_KEY, value); return value; } @Override - protected Map>> getMetaResults(CheckOrigin origin) { + public @NonNull Map>> getMetaResults(CheckOrigin origin) { return new MonitoredMetaMap(super.getMetaResults(origin), origin); } @Override - public int getWeight(CheckOrigin origin) { - int value = super.getWeight(origin); - this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), "weight", StringResult.of(String.valueOf(value))); + public @NonNull IntegerResult getWeight(CheckOrigin origin) { + IntegerResult value = super.getWeight(origin); + this.plugin.getVerboseHandler().offerMetaCheckEvent(origin, this.metadata.getVerboseCheckInfo(), this.metadata.getQueryOptions(), "weight", value.asStringResult()); return value; } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/Group.java b/common/src/main/java/me/lucko/luckperms/common/model/Group.java index 54ff97955..66cb5fc5d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/Group.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/Group.java @@ -28,6 +28,7 @@ import me.lucko.luckperms.common.api.implementation.ApiGroup; import me.lucko.luckperms.common.cache.Cache; import me.lucko.luckperms.common.cacheddata.GroupCachedDataManager; +import me.lucko.luckperms.common.cacheddata.result.IntegerResult; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; @@ -36,13 +37,13 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.luckperms.api.node.NodeType; import net.luckperms.api.node.types.DisplayNameNode; +import net.luckperms.api.node.types.WeightNode; import net.luckperms.api.query.QueryOptions; import org.checkerframework.checker.nullness.qual.NonNull; import java.util.Locale; import java.util.Optional; -import java.util.OptionalInt; public class Group extends PermissionHolder { private final ApiGroup apiProxy = new ApiGroup(this); @@ -55,7 +56,7 @@ public class Group extends PermissionHolder { /** * Caches the groups weight */ - private final Cache weightCache = new WeightCache(this); + private final Cache> weightCache = new WeightCache(this); /** * Caches the groups display name @@ -143,7 +144,7 @@ public Optional calculateDisplayName(QueryOptions queryOptions) { } @Override - public OptionalInt getWeight() { + public IntegerResult getWeightResult() { return this.weightCache.get(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index a4ebf6d7f..50e3bc32c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -28,6 +28,7 @@ import com.google.common.collect.Iterables; import me.lucko.luckperms.common.cacheddata.HolderCachedDataManager; +import me.lucko.luckperms.common.cacheddata.result.IntegerResult; import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator; import me.lucko.luckperms.common.inheritance.InheritanceComparator; import me.lucko.luckperms.common.inheritance.InheritanceGraph; @@ -49,6 +50,7 @@ import net.luckperms.api.node.NodeEqualityPredicate; import net.luckperms.api.node.NodeType; import net.luckperms.api.node.types.InheritanceNode; +import net.luckperms.api.node.types.WeightNode; import net.luckperms.api.query.Flag; import net.luckperms.api.query.QueryOptions; import net.luckperms.api.util.Tristate; @@ -400,9 +402,9 @@ public MetaAccumulator accumulateMeta(MetaAccumulator accumulator, QueryOptions } // accumulate weight - OptionalInt w = holder.getWeight(); - if (w.isPresent()) { - accumulator.accumulateWeight(w.getAsInt()); + IntegerResult weight = holder.getWeightResult(); + if (!weight.isNull()) { + accumulator.accumulateWeight(weight); } } @@ -591,8 +593,13 @@ public boolean clearNodes(DataType dataType, ContextSet contextSet, boolean give return true; } + public IntegerResult getWeightResult() { + return IntegerResult.nullResult(); + } + public OptionalInt getWeight() { - return OptionalInt.empty(); + IntegerResult result = getWeightResult(); + return result.isNull() ? OptionalInt.empty() : OptionalInt.of(result.intResult()); } private static final class MergedNodeResult implements DataMutateResult.WithMergedNode { diff --git a/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java b/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java index e261c36f4..a62d1895a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/WeightCache.java @@ -26,6 +26,7 @@ package me.lucko.luckperms.common.model; import me.lucko.luckperms.common.cache.Cache; +import me.lucko.luckperms.common.cacheddata.result.IntegerResult; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.query.QueryOptionsImpl; @@ -36,12 +37,11 @@ import java.util.Locale; import java.util.Map; -import java.util.OptionalInt; /** * Cache instance to supply the weight of a {@link Group}. */ -public class WeightCache extends Cache { +public class WeightCache extends Cache> { private final Group group; public WeightCache(Group group) { @@ -49,27 +49,24 @@ public WeightCache(Group group) { } @Override - protected @NonNull OptionalInt supply() { - boolean seen = false; - int weight = 0; + protected @NonNull IntegerResult supply() { + IntegerResult weight = null; for (WeightNode n : this.group.getOwnNodes(NodeType.WEIGHT, QueryOptionsImpl.DEFAULT_NON_CONTEXTUAL)) { int value = n.getWeight(); - if (!seen || value > weight) { - seen = true; - weight = value; + if (weight == null || value > weight.intResult()) { + weight = IntegerResult.of(n); } } - if (!seen) { + if (weight == null) { Map configWeights = this.group.getPlugin().getConfiguration().get(ConfigKeys.GROUP_WEIGHTS); Integer value = configWeights.get(this.group.getIdentifier().getName().toLowerCase(Locale.ROOT)); if (value != null) { - seen = true; - weight = value; + weight = IntegerResult.of(value); } } - return seen ? OptionalInt.of(weight) : OptionalInt.empty(); + return weight != null ? weight : IntegerResult.nullResult(); } } diff --git a/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java b/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java index 47b35852d..3e5005b86 100644 --- a/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java +++ b/forge/src/main/java/me/lucko/luckperms/forge/service/ForgePermissionHandler.java @@ -43,6 +43,7 @@ import net.minecraftforge.server.permission.handler.IPermissionHandler; import net.minecraftforge.server.permission.nodes.PermissionDynamicContext; import net.minecraftforge.server.permission.nodes.PermissionNode; +import net.minecraftforge.server.permission.nodes.PermissionType; import net.minecraftforge.server.permission.nodes.PermissionTypes; import java.util.Collection; @@ -111,28 +112,37 @@ public T getOfflinePermission(UUID player, PermissionNode node, Permissio @SuppressWarnings("unchecked") private static T getPermissionValue(User user, QueryOptions queryOptions, PermissionNode node, PermissionDynamicContext... context) { queryOptions = appendContextToQueryOptions(queryOptions, context); + String key = node.getNodeName(); + PermissionType type = node.getType(); - if (node.getType() == PermissionTypes.BOOLEAN) { + // permission check + if (type == PermissionTypes.BOOLEAN) { PermissionCache cache = user.getCachedData().getPermissionData(queryOptions); - Tristate value = cache.checkPermission(node.getNodeName(), CheckOrigin.PLATFORM_API_HAS_PERMISSION).result(); + Tristate value = cache.checkPermission(key, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result(); if (value != Tristate.UNDEFINED) { return (T) (Boolean) value.asBoolean(); } } - if (node.getType() == PermissionTypes.INTEGER) { + // meta lookup + if (node.getType() == PermissionTypes.STRING) { MetaCache cache = user.getCachedData().getMetaData(queryOptions); - Integer value = cache.getMetaValue(node.getNodeName(), Integer::parseInt).orElse(null); + String value = cache.getMetaOrChatMetaValue(node.getNodeName(), CheckOrigin.PLATFORM_API); if (value != null) { return (T) value; } } - if (node.getType() == PermissionTypes.STRING) { + // meta lookup (integer) + if (node.getType() == PermissionTypes.INTEGER) { MetaCache cache = user.getCachedData().getMetaData(queryOptions); - String value = cache.getMetaValue(node.getNodeName()); + String value = cache.getMetaOrChatMetaValue(node.getNodeName(), CheckOrigin.PLATFORM_API); if (value != null) { - return (T) value; + try { + return (T) Integer.valueOf(Integer.parseInt(value)); + } catch (IllegalArgumentException e) { + // ignore + } } } From fbc0787a1eaf3178e4a8ae7d3c672fa1ef1dd15a Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 17 Jul 2022 11:54:14 +0100 Subject: [PATCH 102/314] Implement Fabric meta/options API --- fabric/build.gradle | 2 +- .../luckperms/fabric/LPFabricPlugin.java | 4 +- ...java => FabricPermissionsApiListener.java} | 42 ++++++++++++++++--- .../fabric/mixin/ServerPlayerEntityMixin.java | 31 ++++++++++++++ .../luckperms/fabric/model/MixinUser.java | 4 ++ 5 files changed, 74 insertions(+), 9 deletions(-) rename fabric/src/main/java/me/lucko/luckperms/fabric/listeners/{PermissionCheckListener.java => FabricPermissionsApiListener.java} (66%) diff --git a/fabric/build.gradle b/fabric/build.gradle index d557231d4..889fbd606 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -29,7 +29,7 @@ dependencies { modImplementation(fabricApi.module(it, '0.55.2+1.19')) } - include(modImplementation('me.lucko:fabric-permissions-api:0.1-SNAPSHOT')) + include(modImplementation('me.lucko:fabric-permissions-api:0.2-SNAPSHOT')) implementation project(':common') } diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java index e70a1b9b5..b2d01b231 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java @@ -46,7 +46,7 @@ import me.lucko.luckperms.fabric.listeners.FabricCommandListUpdater; import me.lucko.luckperms.fabric.listeners.FabricConnectionListener; import me.lucko.luckperms.fabric.listeners.FabricOtherListeners; -import me.lucko.luckperms.fabric.listeners.PermissionCheckListener; +import me.lucko.luckperms.fabric.listeners.FabricPermissionsApiListener; import me.lucko.luckperms.fabric.messaging.FabricMessagingFactory; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; @@ -88,7 +88,7 @@ protected void registerFabricListeners() { this.connectionListener = new FabricConnectionListener(this); this.connectionListener.registerListeners(); - new PermissionCheckListener(this).registerListeners(); + new FabricPermissionsApiListener(this).registerListeners(); // Command registration also need to occur early, and will persist across game states as well. this.commandManager = new FabricCommandExecutor(this); diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/PermissionCheckListener.java b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricPermissionsApiListener.java similarity index 66% rename from fabric/src/main/java/me/lucko/luckperms/fabric/listeners/PermissionCheckListener.java rename to fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricPermissionsApiListener.java index cfef3c0f6..18c172397 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/PermissionCheckListener.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/listeners/FabricPermissionsApiListener.java @@ -25,7 +25,9 @@ package me.lucko.luckperms.fabric.listeners; +import me.lucko.fabric.api.permissions.v0.OptionRequestEvent; import me.lucko.fabric.api.permissions.v0.PermissionCheckEvent; +import me.lucko.luckperms.common.cacheddata.result.StringResult; import me.lucko.luckperms.common.cacheddata.result.TristateResult; import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; @@ -41,31 +43,44 @@ import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Optional; + /** * Listener to route permission checks made via fabric-permissions-api to LuckPerms. */ -public class PermissionCheckListener { +public class FabricPermissionsApiListener { private final LPFabricPlugin plugin; - public PermissionCheckListener(LPFabricPlugin plugin) { + public FabricPermissionsApiListener(LPFabricPlugin plugin) { this.plugin = plugin; } public void registerListeners() { PermissionCheckEvent.EVENT.register(this::onPermissionCheck); + OptionRequestEvent.EVENT.register(this::onOptionRequest); } private @NonNull TriState onPermissionCheck(CommandSource source, String permission) { if (source instanceof ServerCommandSource) { Entity entity = ((ServerCommandSource) source).getEntity(); if (entity instanceof ServerPlayerEntity) { - return onPlayerPermissionCheck((ServerPlayerEntity) entity, permission); + return playerPermissionCheck((ServerPlayerEntity) entity, permission); } } - return onOtherPermissionCheck(source, permission); + return otherPermissionCheck(source, permission); } - private TriState onPlayerPermissionCheck(ServerPlayerEntity player, String permission) { + private @NonNull Optional onOptionRequest(CommandSource source, String key) { + if (source instanceof ServerCommandSource) { + Entity entity = ((ServerCommandSource) source).getEntity(); + if (entity instanceof ServerPlayerEntity) { + return playerGetOption((ServerPlayerEntity) entity, key); + } + } + return otherGetOption(source, key); + } + + private TriState playerPermissionCheck(ServerPlayerEntity player, String permission) { switch (((MixinUser) player).hasPermission(permission)) { case TRUE: return TriState.TRUE; @@ -78,7 +93,7 @@ private TriState onPlayerPermissionCheck(ServerPlayerEntity player, String permi } } - private TriState onOtherPermissionCheck(CommandSource source, String permission) { + private TriState otherPermissionCheck(CommandSource source, String permission) { if (source instanceof ServerCommandSource) { String name = ((ServerCommandSource) source).getName(); VerboseCheckTarget target = VerboseCheckTarget.internal(name); @@ -90,4 +105,19 @@ private TriState onOtherPermissionCheck(CommandSource source, String permission) return TriState.DEFAULT; } + private Optional playerGetOption(ServerPlayerEntity player, String key) { + return Optional.ofNullable(((MixinUser) player).getOption(key)); + } + + private Optional otherGetOption(CommandSource source, String key) { + if (source instanceof ServerCommandSource) { + String name = ((ServerCommandSource) source).getName(); + VerboseCheckTarget target = VerboseCheckTarget.internal(name); + + this.plugin.getVerboseHandler().offerMetaCheckEvent(CheckOrigin.PLATFORM_API, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, key, StringResult.nullResult()); + } + + return Optional.empty(); + } + } diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java index 2ee381204..fe3990dec 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java @@ -25,6 +25,7 @@ package me.lucko.luckperms.fabric.mixin; +import me.lucko.luckperms.common.cacheddata.type.MetaCache; import me.lucko.luckperms.common.cacheddata.type.PermissionCache; import me.lucko.luckperms.common.context.manager.QueryOptionsCache; import me.lucko.luckperms.common.locale.TranslationManager; @@ -136,6 +137,36 @@ public Tristate hasPermission(String permission, QueryOptions queryOptions) { return data.checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result(); } + @Override + public String getOption(String key) { + if (key == null) { + throw new NullPointerException("key"); + } + if (this.luckperms$user == null || this.luckperms$queryOptions == null) { + // "fake" players will have our mixin, but won't have been initialised. + return null; + } + return getOption(key, this.luckperms$queryOptions.getQueryOptions()); + } + + @Override + public String getOption(String key, QueryOptions queryOptions) { + if (key == null) { + throw new NullPointerException("key"); + } + if (queryOptions == null) { + throw new NullPointerException("queryOptions"); + } + + final User user = this.luckperms$user; + if (user == null || this.luckperms$queryOptions == null) { + // "fake" players will have our mixin, but won't have been initialised. + return null; + } + + MetaCache cache = user.getCachedData().getMetaData(queryOptions); + return cache.getMetaOrChatMetaValue(key, CheckOrigin.PLATFORM_API); + } @Inject(at = @At("TAIL"), method = "copyFrom") private void luckperms_copyFrom(ServerPlayerEntity oldPlayer, boolean alive, CallbackInfo ci) { diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/model/MixinUser.java b/fabric/src/main/java/me/lucko/luckperms/fabric/model/MixinUser.java index 2a6911615..370f212d6 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/model/MixinUser.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/model/MixinUser.java @@ -68,4 +68,8 @@ public interface MixinUser { Tristate hasPermission(String permission, QueryOptions queryOptions); + String getOption(String key); + + String getOption(String key, QueryOptions queryOptions); + } From 8b0d6fd5d2b5bcec3cfc92d80ee798f4304f4b0d Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 18 Jul 2022 21:38:44 +0100 Subject: [PATCH 103/314] Fix compile --- .../me/lucko/luckperms/common/webeditor/WebEditorRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java index b6d4556af..07af8e80b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorRequest.java @@ -240,7 +240,7 @@ public static void includeMatchingUsers(List holders, CollectioncomparingInt(u -> u.getCachedData().getMetaData(QueryOptions.nonContextual()).getWeight(CheckOrigin.INTERNAL)).reversed() + .comparingInt(u -> u.getCachedData().getMetaData(QueryOptions.nonContextual()).getWeight(CheckOrigin.INTERNAL).intResult()).reversed() // then, prioritise users we actually have a username for .thenComparing(u -> u.getUsername().isPresent(), ((Comparator) Boolean::compare).reversed()) // then sort according to their username From d36341c13901703d2e6e63e57a9887d25fd084c3 Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 20 Jul 2022 22:46:22 +0100 Subject: [PATCH 104/314] Implement standalone/cli app --- .github/workflows/ci.yml | 108 ++++ .../net/luckperms/api/platform/Platform.java | 3 +- build.gradle | 3 +- settings.gradle | 5 +- standalone/app/build.gradle | 41 ++ .../standalone/app/LuckPermsApplication.java | 118 ++++ .../app/integration/CommandExecutor.java | 40 ++ .../app/integration/DockerCommandSocket.java | 93 +++ .../app/integration/ShutdownCallback.java | 37 ++ .../app/integration/SingletonPlayer.java | 58 ++ .../app/integration/TerminalInterface.java | 99 +++ .../standalone/app/utils/AnsiUtils.java | 131 ++++ standalone/build.gradle | 40 ++ standalone/docker/Dockerfile | 22 + standalone/loader/build.gradle | 37 ++ .../standalone/loader/StandaloneLoader.java | 97 +++ .../loader/src/main/resources/log4j2.xml | 14 + .../standalone/LPStandaloneBootstrap.java | 199 ++++++ .../standalone/LPStandalonePlugin.java | 208 +++++++ .../StandaloneCalculatorFactory.java | 70 +++ .../standalone/StandaloneCommandManager.java | 59 ++ .../standalone/StandaloneConfigAdapter.java | 47 ++ .../StandaloneMessagingFactory.java | 41 ++ .../StandaloneSchedulerAdapter.java | 44 ++ .../standalone/StandaloneSenderFactory.java | 80 +++ .../dummy/StandaloneContextManager.java | 69 +++ .../StandaloneDummyConnectionListener.java | 35 ++ .../standalone/dummy/StandaloneEventBus.java | 42 ++ standalone/src/main/resources/config.yml | 567 ++++++++++++++++++ 29 files changed, 2404 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 standalone/app/build.gradle create mode 100644 standalone/app/src/main/java/me/lucko/luckperms/standalone/app/LuckPermsApplication.java create mode 100644 standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/CommandExecutor.java create mode 100644 standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/DockerCommandSocket.java create mode 100644 standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/ShutdownCallback.java create mode 100644 standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java create mode 100644 standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/TerminalInterface.java create mode 100644 standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java create mode 100644 standalone/build.gradle create mode 100644 standalone/docker/Dockerfile create mode 100644 standalone/loader/build.gradle create mode 100644 standalone/loader/src/main/java/me/lucko/luckperms/standalone/loader/StandaloneLoader.java create mode 100644 standalone/loader/src/main/resources/log4j2.xml create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/LPStandaloneBootstrap.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/LPStandalonePlugin.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/StandaloneCalculatorFactory.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/StandaloneCommandManager.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/StandaloneConfigAdapter.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/StandaloneMessagingFactory.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/StandaloneSchedulerAdapter.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/StandaloneSenderFactory.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/dummy/StandaloneContextManager.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/dummy/StandaloneDummyConnectionListener.java create mode 100644 standalone/src/main/java/me/lucko/luckperms/standalone/dummy/StandaloneEventBus.java create mode 100644 standalone/src/main/resources/config.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..832296bd5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,108 @@ +name: Build Gradle and Publish Docker image + +on: + push: + branches: + - 'master' + tags: + - 'v*' + pull_request: + branches: + - 'master' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: luckperms-standalone + +jobs: + build-gradle: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Run build with Gradle wrapper + run: ./gradlew build + + - name: Upload all artifacts + uses: actions/upload-artifact@v3 + with: + name: jars + path: | + bukkit/loader/build/libs/LuckPerms-Bukkit-*.jar + bukkit-legacy/loader/build/libs/LuckPerms-Bukkit-Legacy-*.jar + bungee/loader/build/libs/LuckPerms-Bungee-*.jar + sponge/loader/build/libs/LuckPerms-Sponge-*.jar + nukkit/loader/build/libs/LuckPerms-Nukkit-*.jar + velocity/build/libs/LuckPerms-Velocity-*.jar + fabric/build/libs/LuckPerms-Fabric-*.jar + forge/loader/build/libs/LuckPerms-Forge-*.jar + standalone/loader/build/libs/LuckPerms-Standalone-*.jar + + - name: Upload standalone artifact + uses: actions/upload-artifact@v3 + with: + name: standalone-binary + path: standalone/loader/build/libs/LuckPerms-Standalone-*.jar + + + build-docker: + needs: build-gradle + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Retrieve saved standalone jar artifact + uses: actions/download-artifact@v3 + with: + name: standalone-binary + path: standalone/docker/ + + - name: Display structure of downloaded files + run: ls -R + working-directory: standalone/docker/ + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Log in to the Container registry + uses: docker/login-action@v1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + flavor: | + latest=${{ github.ref == 'refs/heads/master' }} + + - name: Build and push Docker image + uses: docker/build-push-action@v2 + with: + context: standalone/docker/ + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/api/src/main/java/net/luckperms/api/platform/Platform.java b/api/src/main/java/net/luckperms/api/platform/Platform.java index 92b4fd09f..bc31a34a8 100644 --- a/api/src/main/java/net/luckperms/api/platform/Platform.java +++ b/api/src/main/java/net/luckperms/api/platform/Platform.java @@ -76,7 +76,8 @@ enum Type { NUKKIT("Nukkit"), VELOCITY("Velocity"), FABRIC("Fabric"), - FORGE("Forge"); + FORGE("Forge"), + STANDALONE("Standalone"); private final String friendlyName; diff --git a/build.gradle b/build.gradle index 44022515d..d8ac4cb96 100644 --- a/build.gradle +++ b/build.gradle @@ -52,7 +52,8 @@ subprojects { repositories { mavenCentral() - maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } + maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } + maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } maven { url 'https://repo.lucko.me/' } maven { url 'https://libraries.minecraft.net/' } } diff --git a/settings.gradle b/settings.gradle index 06b95aad0..b03ef276d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,5 +30,8 @@ include ( 'sponge:loader', 'sponge:sponge-service', 'sponge:sponge-service-api8', - 'velocity' + 'velocity', + 'standalone', + 'standalone:loader', + 'standalone:app' ) diff --git a/standalone/app/build.gradle b/standalone/app/build.gradle new file mode 100644 index 000000000..91f342aab --- /dev/null +++ b/standalone/app/build.gradle @@ -0,0 +1,41 @@ +plugins { + id 'net.kyori.blossom' version '1.3.0' + id 'java-library' +} + +dependencies { + implementation project(':api') + + api 'org.apache.logging.log4j:log4j-core:2.17.2' + api 'org.apache.logging.log4j:log4j-slf4j-impl:2.17.2' + api 'net.minecrell:terminalconsoleappender:1.3.0' + api 'org.jline:jline-terminal-jansi:3.20.0' + + api 'com.google.code.gson:gson:2.9.0' + api 'com.google.guava:guava:31.1-jre' + + api('net.kyori:adventure-api:4.11.0') { + exclude(module: 'adventure-bom') + exclude(module: 'checker-qual') + exclude(module: 'annotations') + } + api('net.kyori:adventure-text-serializer-gson:4.11.0') { + exclude(module: 'adventure-bom') + exclude(module: 'adventure-api') + exclude(module: 'gson') + } + api('net.kyori:adventure-text-serializer-legacy:4.11.0') { + exclude(module: 'adventure-bom') + exclude(module: 'adventure-api') + } + api('net.kyori:adventure-text-serializer-plain:4.11.0') { + exclude(module: 'adventure-bom') + exclude(module: 'adventure-api') + } + api('net.kyori:ansi:1.0.0-SNAPSHOT') +} + +blossom { + replaceTokenIn('src/main/java/me/lucko/luckperms/standalone/app/LuckPermsApplication.java') + replaceToken '@version@', project.ext.fullVersion +} \ No newline at end of file diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/LuckPermsApplication.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/LuckPermsApplication.java new file mode 100644 index 000000000..a3da8e1e5 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/LuckPermsApplication.java @@ -0,0 +1,118 @@ +/* + * 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.standalone.app; + +import me.lucko.luckperms.standalone.app.integration.CommandExecutor; +import me.lucko.luckperms.standalone.app.integration.DockerCommandSocket; +import me.lucko.luckperms.standalone.app.integration.ShutdownCallback; +import me.lucko.luckperms.standalone.app.integration.TerminalInterface; + +import net.luckperms.api.LuckPerms; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * The LuckPerms standalone application. + */ +public class LuckPermsApplication implements AutoCloseable { + + /** A logger instance */ + public static final Logger LOGGER = LogManager.getLogger(LuckPermsApplication.class); + + /** A callback to shutdown the application via the loader bootstrap. */ + private final ShutdownCallback shutdownCallback; + + /** The instance of the LuckPerms API available within the app */ + private LuckPerms luckPermsApi; + /** A command executor interface to run LuckPerms commands */ + private CommandExecutor commandExecutor; + + /** If the application is running */ + private final AtomicBoolean running = new AtomicBoolean(true); + + /** The docker command socket */ + private DockerCommandSocket dockerCommandSocket; + + public LuckPermsApplication(ShutdownCallback shutdownCallback) { + this.shutdownCallback = shutdownCallback; + } + + /** + * Start the app + */ + public void start(String[] args) { + TerminalInterface terminal = new TerminalInterface(this, this.commandExecutor); + + List arguments = Arrays.asList(args); + if (arguments.contains("--docker")) { + this.dockerCommandSocket = DockerCommandSocket.createAndStart(3000, terminal); + } + + terminal.start(); // blocking + } + + public void requestShutdown() { + this.shutdownCallback.shutdown(); + } + + @Override + public void close() { + this.running.set(false); + + if (this.dockerCommandSocket != null) { + try { + this.dockerCommandSocket.close(); + } catch (IOException e) { + LOGGER.warn(e); + } + } + } + + public AtomicBoolean runningState() { + return this.running; + } + + // called before start() + public void setApi(LuckPerms luckPermsApi) { + this.luckPermsApi = luckPermsApi; + } + + // called before start() + public void setCommandExecutor(CommandExecutor commandExecutor) { + this.commandExecutor = commandExecutor; + } + + public String getVersion() { + return "@version@"; + } + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/CommandExecutor.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/CommandExecutor.java new file mode 100644 index 000000000..8cdec14c9 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/CommandExecutor.java @@ -0,0 +1,40 @@ +/* + * 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.standalone.app.integration; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * Minimal command executor interface. + */ +public interface CommandExecutor { + + CompletableFuture execute(String command); + + List tabComplete(String command); + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/DockerCommandSocket.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/DockerCommandSocket.java new file mode 100644 index 000000000..f331d9aa1 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/DockerCommandSocket.java @@ -0,0 +1,93 @@ +/* + * 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.standalone.app.integration; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.function.Consumer; + +/** + * Simple/dumb socket that listens for connections on a given port, + * reads the input to a string, then executes it as a command. + * + * Combined with a small sh/nc program, this makes it easy to execute + * commands against a standalone instance of LP in a Docker container. + */ +public class DockerCommandSocket extends ServerSocket implements Runnable { + private static final Logger LOGGER = LogManager.getLogger(DockerCommandSocket.class); + + public static DockerCommandSocket createAndStart(int port, TerminalInterface terminal) { + DockerCommandSocket socket = null; + + try { + socket = new DockerCommandSocket(port, terminal::runCommand); + + Thread thread = new Thread(socket, "docker-command-socket"); + thread.setDaemon(true); + thread.start(); + + LOGGER.info("Created Docker command socket on port 3000"); + } catch (Exception e) { + LOGGER.error("Error starting docker command socket", e); + } + + return socket; + } + + private final Consumer callback; + + public DockerCommandSocket(int port, Consumer callback) throws IOException { + super(port); + this.callback = callback; + } + + @Override + public void run() { + while (!isClosed()) { + try (Socket socket = accept()) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { + String cmd; + while ((cmd = reader.readLine()) != null) { + LOGGER.info("Executing command from Docker: " + cmd); + this.callback.accept(cmd); + } + } + } catch (IOException e) { + if (e instanceof SocketException && e.getMessage().equals("Socket closed")) { + return; + } + LOGGER.error("Error processing input from the Docker socket", e); + } + } + } +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/ShutdownCallback.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/ShutdownCallback.java new file mode 100644 index 000000000..1fcbd0212 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/ShutdownCallback.java @@ -0,0 +1,37 @@ +/* + * 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.standalone.app.integration; + +/** + * Shutdown callback for the whole standalone app. + * + * (in practice this is always implemented by the StandaloneLoader class) + */ +public interface ShutdownCallback { + + void shutdown(); + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java new file mode 100644 index 000000000..9e195ffd1 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java @@ -0,0 +1,58 @@ +/* + * 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.standalone.app.integration; + +import me.lucko.luckperms.standalone.app.LuckPermsApplication; +import me.lucko.luckperms.standalone.app.utils.AnsiUtils; + +import net.kyori.adventure.text.Component; + +import java.util.UUID; + +/** + * Dummy/singleton player class used by the standalone plugin. + * + * In various places (ContextManager, SenderFactory, ..) the platform "player" type is used + * as a generic parameter. This class acts as this type for the standalone plugin. + */ +public class SingletonPlayer { + public static final SingletonPlayer INSTANCE = new SingletonPlayer(); + + private static final UUID UUID = new UUID(0, 0); + + public String getName() { + return "StandaloneUser"; + } + + public UUID getUniqueId() { + return UUID; + } + + public void printStdout(Component component) { + LuckPermsApplication.LOGGER.info(AnsiUtils.format(component)); + } + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/TerminalInterface.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/TerminalInterface.java new file mode 100644 index 000000000..f55a94c59 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/TerminalInterface.java @@ -0,0 +1,99 @@ +/* + * 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.standalone.app.integration; + +import me.lucko.luckperms.standalone.app.LuckPermsApplication; + +import net.minecrell.terminalconsole.SimpleTerminalConsole; + +import org.jline.reader.Candidate; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; +import org.jline.reader.ParsedLine; + +import java.util.List; + +/** + * The terminal/console-style interface presented to the user. + */ +public class TerminalInterface extends SimpleTerminalConsole { + private final LuckPermsApplication application; + private final CommandExecutor commandExecutor; + + public TerminalInterface(LuckPermsApplication application, CommandExecutor commandExecutor) { + this.application = application; + this.commandExecutor = commandExecutor; + } + + @Override + protected LineReader buildReader(LineReaderBuilder builder) { + return super.buildReader(builder + .appName("LuckPerms") + .completer(this::completeCommand) + ); + } + + @Override + protected boolean isRunning() { + return this.application.runningState().get(); + } + + @Override + protected void shutdown() { + this.application.requestShutdown(); + } + + @Override + public void runCommand(String command) { + command = stripSlashLp(command); + + if (command.equals("stop") || command.equals("exit")) { + this.application.requestShutdown(); + return; + } + + this.commandExecutor.execute(command); + } + + private void completeCommand(LineReader reader, ParsedLine line, List candidates) { + String cmdLine = stripSlashLp(line.line()); + + for (String suggestion : this.commandExecutor.tabComplete(cmdLine)) { + candidates.add(new Candidate(suggestion)); + } + } + + private static String stripSlashLp(String command) { + if (command.startsWith("/")) { + command = command.substring(1); + } + if (command.startsWith("lp ")) { + command = command.substring(3); + } + return command; + } + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java new file mode 100644 index 000000000..ab2ac5086 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java @@ -0,0 +1,131 @@ +/* + * 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.standalone.app.utils; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.kyori.adventure.text.flattener.FlattenerListener; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.ansi.ANSIComponentRenderer; +import net.kyori.ansi.StyleOps; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +/** + * Utility to format a {@link Component} as an ANSI string. + */ +public final class AnsiUtils { + private AnsiUtils() {} + + public static String format(Component component) { + ANSIComponentRenderer.ToString
  • Project Website