diff --git a/api/src/main/java/me/lucko/luckperms/api/platform/PlatformType.java b/api/src/main/java/me/lucko/luckperms/api/platform/PlatformType.java index c66ffa5f8..c5da9bce5 100644 --- a/api/src/main/java/me/lucko/luckperms/api/platform/PlatformType.java +++ b/api/src/main/java/me/lucko/luckperms/api/platform/PlatformType.java @@ -37,7 +37,8 @@ public enum PlatformType { BUKKIT("Bukkit"), BUNGEE("Bungee"), SPONGE("Sponge"), - NUKKIT("Nukkit"); + NUKKIT("Nukkit"), + VELOCITY("Velocity"); private final String friendlyName; diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BukkitMessagingFactory.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BukkitMessagingFactory.java index e9d57c7d1..2631fa71a 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BukkitMessagingFactory.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BukkitMessagingFactory.java @@ -42,9 +42,9 @@ public class BukkitMessagingFactory extends MessagingFactory { @Override protected InternalMessagingService getServiceFor(String messagingType) { - if (messagingType.equals("bungee")) { + if (messagingType.equals("pluginmsg") || messagingType.equals("bungee") || messagingType.equals("velocity")) { try { - return new LuckPermsMessagingService(getPlugin(), new BungeeMessengerProvider()); + return new LuckPermsMessagingService(getPlugin(), new PluginMessageMessengerProvider()); } catch (Exception e) { e.printStackTrace(); } @@ -63,20 +63,20 @@ public class BukkitMessagingFactory extends MessagingFactory { return super.getServiceFor(messagingType); } - private class BungeeMessengerProvider implements MessengerProvider { + private class PluginMessageMessengerProvider implements MessengerProvider { @Nonnull @Override public String getName() { - return "Bungee"; + return "PluginMessage"; } @Nonnull @Override public Messenger obtain(@Nonnull IncomingMessageConsumer incomingMessageConsumer) { - BungeeMessenger bungeeMessaging = new BungeeMessenger(getPlugin(), incomingMessageConsumer); - bungeeMessaging.init(); - return bungeeMessaging; + PluginMessageMessenger messenger = new PluginMessageMessenger(getPlugin(), incomingMessageConsumer); + messenger.init(); + return messenger; } } @@ -91,9 +91,9 @@ public class BukkitMessagingFactory extends MessagingFactory { @Nonnull @Override public Messenger obtain(@Nonnull IncomingMessageConsumer incomingMessageConsumer) { - LilyPadMessenger lilyPadMessaging = new LilyPadMessenger(getPlugin(), incomingMessageConsumer); - lilyPadMessaging.init(); - return lilyPadMessaging; + LilyPadMessenger messenger = new LilyPadMessenger(getPlugin(), incomingMessageConsumer); + messenger.init(); + return messenger; } } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BungeeMessenger.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/PluginMessageMessenger.java similarity index 90% rename from bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BungeeMessenger.java rename to bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/PluginMessageMessenger.java index 690487742..225747ede 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BungeeMessenger.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/PluginMessageMessenger.java @@ -46,13 +46,13 @@ import javax.annotation.Nonnull; /** * An implementation of {@link Messenger} using the plugin messaging channels. */ -public class BungeeMessenger implements Messenger, PluginMessageListener { +public class PluginMessageMessenger implements Messenger, PluginMessageListener { private static final String CHANNEL = "luckperms:update"; private final LPBukkitPlugin plugin; private final IncomingMessageConsumer consumer; - public BungeeMessenger(LPBukkitPlugin plugin, IncomingMessageConsumer consumer) { + public PluginMessageMessenger(LPBukkitPlugin plugin, IncomingMessageConsumer consumer) { this.plugin = plugin; this.consumer = consumer; } @@ -73,7 +73,7 @@ public class BungeeMessenger implements Messenger, PluginMessageListener { new BukkitRunnable() { @Override public void run() { - Collection players = BungeeMessenger.this.plugin.getBootstrap().getServer().getOnlinePlayers(); + Collection players = PluginMessageMessenger.this.plugin.getBootstrap().getServer().getOnlinePlayers(); Player p = Iterables.getFirst(players, null); if (p == null) { return; @@ -84,7 +84,7 @@ public class BungeeMessenger implements Messenger, PluginMessageListener { byte[] data = out.toByteArray(); - p.sendPluginMessage(BungeeMessenger.this.plugin.getBootstrap(), CHANNEL, data); + p.sendPluginMessage(PluginMessageMessenger.this.plugin.getBootstrap(), CHANNEL, data); cancel(); } }.runTaskTimer(this.plugin.getBootstrap(), 1L, 100L); diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index 71bc6587d..fd0f92fec 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -205,9 +205,9 @@ watch-files: true # => 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 'none' and SQL storage is in use. Set to 'notsql' to disable this. -# => bungee Uses the plugin messaging channels to communicate with the proxy. +# => 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 BungeeCord proxy. +# Won't work if you have more than one proxy. # => lilypad Uses LilyPad pub-sub to push changes. You need to have the LilyPad-Connect plugin # installed. # => redis Uses Redis pub-sub to push changes. Your server connection info must be configured diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessagingFactory.java b/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessagingFactory.java index 40491ed9c..311462a3a 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessagingFactory.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessagingFactory.java @@ -42,9 +42,9 @@ public class BungeeMessagingFactory extends MessagingFactory { @Override protected InternalMessagingService getServiceFor(String messagingType) { - if (messagingType.equals("bungee")) { + if (messagingType.equals("pluginmsg") || messagingType.equals("bungee")) { try { - return new LuckPermsMessagingService(getPlugin(), new BungeeMessengerProvider()); + return new LuckPermsMessagingService(getPlugin(), new PluginMessageMessengerProvider()); } catch (Exception e) { e.printStackTrace(); } @@ -63,20 +63,20 @@ public class BungeeMessagingFactory extends MessagingFactory { return super.getServiceFor(messagingType); } - private class BungeeMessengerProvider implements MessengerProvider { + private class PluginMessageMessengerProvider implements MessengerProvider { @Nonnull @Override public String getName() { - return "Bungee"; + return "PluginMessage"; } @Nonnull @Override public Messenger obtain(@Nonnull IncomingMessageConsumer incomingMessageConsumer) { - BungeeMessenger bungeeMessaging = new BungeeMessenger(getPlugin(), incomingMessageConsumer); - bungeeMessaging.init(); - return bungeeMessaging; + PluginMessageMessenger messenger = new PluginMessageMessenger(getPlugin(), incomingMessageConsumer); + messenger.init(); + return messenger; } } @@ -91,9 +91,9 @@ public class BungeeMessagingFactory extends MessagingFactory { @Nonnull @Override public Messenger obtain(@Nonnull IncomingMessageConsumer incomingMessageConsumer) { - RedisBungeeMessenger redisBungeeMessaging = new RedisBungeeMessenger(getPlugin(), incomingMessageConsumer); - redisBungeeMessaging.init(); - return redisBungeeMessaging; + RedisBungeeMessenger messenger = new RedisBungeeMessenger(getPlugin(), incomingMessageConsumer); + messenger.init(); + return messenger; } } } diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessenger.java b/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/PluginMessageMessenger.java similarity index 93% rename from bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessenger.java rename to bungee/src/main/java/me/lucko/luckperms/bungee/messaging/PluginMessageMessenger.java index 2bd0f720f..4a68fff83 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessenger.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/PluginMessageMessenger.java @@ -45,13 +45,13 @@ import javax.annotation.Nonnull; /** * An implementation of {@link Messenger} using the plugin messaging channels. */ -public class BungeeMessenger implements Messenger, Listener { +public class PluginMessageMessenger implements Messenger, Listener { private static final String CHANNEL = "luckperms:update"; private final LPBungeePlugin plugin; private final IncomingMessageConsumer consumer; - public BungeeMessenger(LPBungeePlugin plugin, IncomingMessageConsumer consumer) { + public PluginMessageMessenger(LPBungeePlugin plugin, IncomingMessageConsumer consumer) { this.plugin = plugin; this.consumer = consumer; } @@ -74,7 +74,7 @@ public class BungeeMessenger implements Messenger, Listener { byte[] data = out.toByteArray(); for (ServerInfo server : this.plugin.getBootstrap().getProxy().getServers().values()) { - server.sendData(CHANNEL, data, true); + server.sendData(CHANNEL, data, false); } } @@ -99,7 +99,7 @@ public class BungeeMessenger implements Messenger, Listener { // Forward to other servers this.plugin.getBootstrap().getScheduler().executeAsync(() -> { for (ServerInfo server : this.plugin.getBootstrap().getProxy().getServers().values()) { - server.sendData(CHANNEL, data, true); + server.sendData(CHANNEL, data, false); } }); } diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index c270c049a..ddf0b146b 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -203,7 +203,7 @@ watch-files: true # when 'storage-method' is set to MySQL or MariaDB. This is chosen by default if # the option is set to 'none' and SQL storage is in use. Set to 'notsql' to # disable this. -# => bungee Uses the plugin messaging channels to communicate with the proxy. +# => 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 BungeeCord proxy. # => redis Uses Redis pub-sub to push changes. Your server connection info must be diff --git a/common/src/main/java/me/lucko/luckperms/common/config/adapter/ConfigurateConfigAdapter.java b/common/src/main/java/me/lucko/luckperms/common/config/adapter/ConfigurateConfigAdapter.java new file mode 100644 index 000000000..df1b19a20 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/config/adapter/ConfigurateConfigAdapter.java @@ -0,0 +1,123 @@ +/* + * 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.adapter; + +import com.google.common.base.Splitter; +import com.google.common.base.Supplier; + +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.loader.AbstractConfigurationLoader; +import ninja.leaping.configurate.loader.ConfigurationLoader; +import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public abstract class ConfigurateConfigAdapter extends AbstractConfigurationAdapter implements ConfigurationAdapter { + + private final Path path; + private ConfigurationNode root; + + public ConfigurateConfigAdapter(LuckPermsPlugin plugin, Path path) { + super(plugin); + this.path = path; + reload(); + } + + protected abstract ConfigurationLoader createLoader(Path path); + + @Override + public void reload() { + ConfigurationLoader loader = createLoader(this.path); + try { + this.root = loader.load(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private ConfigurationNode resolvePath(String path) { + if (this.root == null) { + throw new RuntimeException("Config is not loaded."); + } + + return this.root.getNode(Splitter.on('.').splitToList(path).toArray()); + } + + @Override + public String getString(String path, String def) { + return resolvePath(path).getString(def); + } + + @Override + public int getInt(String path, int def) { + return resolvePath(path).getInt(def); + } + + @Override + public boolean getBoolean(String path, boolean def) { + return resolvePath(path).getBoolean(def); + } + + @Override + public List getList(String path, List def) { + ConfigurationNode node = resolvePath(path); + if (node.isVirtual()) { + return def; + } + + return node.getList(Object::toString); + } + + @Override + public List getKeys(String path, List def) { + ConfigurationNode node = resolvePath(path); + if (node.isVirtual()) { + return def; + } + + return node.getChildrenMap().keySet().stream().map(Object::toString).collect(Collectors.toList()); + } + + @SuppressWarnings("unchecked") + @Override + public Map getMap(String path, Map def) { + ConfigurationNode node = resolvePath(path); + if (node.isVirtual()) { + return def; + } + + Map m = (Map) node.getValue(Collections.emptyMap()); + return m.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue().toString())); + } +} 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 13caeea3b..4510db407 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 @@ -210,6 +210,13 @@ public enum Dependency { "Dxr1o3EPbpOOmwraqu+cors8O/nKwJnhS5EiPkTb3fc=", Relocation.of("configurate", "ninja{}leaping{}configurate") ), + SNAKEYAML( + "org.yaml", + "snakeyaml", + "1.21", + "5DywaD9wgEuDPfqlrAMv8Uugx1jUoenq62ZAUV34P68=", + Relocation.of("yaml", "org{}yaml{}snakeyaml") + ), CONFIGURATE_HOCON( "me{}lucko{}configurate", "configurate-hocon", 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 89ae1c0be..11a5e819d 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 @@ -153,7 +153,7 @@ public class DependencyManager { try { // apply remap rules List relocations = new ArrayList<>(source.dependency.getRelocations()); - relocations.addAll(this.registry.getLegacyRelocations(source.dependency)); + this.registry.applyRelocationSettings(source.dependency, relocations); if (relocations.isEmpty()) { remappedJars.add(source); 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 a75caae91..b47d78d54 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 @@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.gson.JsonElement; +import me.lucko.luckperms.api.platform.PlatformType; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.dependencies.relocation.Relocation; import me.lucko.luckperms.common.dependencies.relocation.RelocationHandler; @@ -86,19 +87,24 @@ public class DependencyRegistry { return dependencies; } - // support for LuckPerms legacy (bukkit 1.7.10) - public List getLegacyRelocations(Dependency dependency) { - if (RelocationHandler.DEPENDENCIES.contains(dependency)) { - return ImmutableList.of(); + public void applyRelocationSettings(Dependency dependency, List relocations) { + PlatformType type = this.plugin.getBootstrap().getType(); + + // support for LuckPerms legacy (bukkit 1.7.10) + if (!RelocationHandler.DEPENDENCIES.contains(dependency) && JsonElement.class.getName().startsWith("me.lucko")) { + relocations.add(Relocation.of("guava", "com{}google{}common")); + relocations.add(Relocation.of("gson", "com{}google{}gson")); } - if (JsonElement.class.getName().startsWith("me.lucko")) { - return ImmutableList.of( - Relocation.of("guava", "com{}google{}common"), - Relocation.of("gson", "com{}google{}gson") - ); + // don't relocate text when running on Velocity + if (dependency == Dependency.TEXT && type == PlatformType.VELOCITY) { + relocations.remove(Relocation.of("text", "net{}kyori{}text")); + } + + // relocate yaml within configurate when running velocity + if (dependency == Dependency.CONFIGURATE_YAML && type == PlatformType.VELOCITY) { + relocations.add(Relocation.of("yaml", "org{}yaml{}snakeyaml")); } - return ImmutableList.of(); } private static boolean classExists(String className) { diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/classloader/PluginClassLoader.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/classloader/PluginClassLoader.java index e32de39bc..ab25ab62f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/classloader/PluginClassLoader.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/classloader/PluginClassLoader.java @@ -25,6 +25,7 @@ package me.lucko.luckperms.common.dependencies.classloader; +import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Path; @@ -33,8 +34,6 @@ import java.nio.file.Path; */ public interface PluginClassLoader { - void loadJar(URL url); - void loadJar(Path file); } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/classloader/ReflectionClassLoader.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/classloader/ReflectionClassLoader.java index 49be4ced9..5515300ff 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/classloader/ReflectionClassLoader.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/classloader/ReflectionClassLoader.java @@ -55,20 +55,11 @@ public class ReflectionClassLoader implements PluginClassLoader { } } - @Override - public void loadJar(URL url) { - try { - ADD_URL_METHOD.invoke(this.classLoader, url); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } - @Override public void loadJar(Path file) { try { - loadJar(file.toUri().toURL()); - } catch (MalformedURLException e) { + ADD_URL_METHOD.invoke(this.classLoader, file.toUri().toURL()); + } catch (IllegalAccessException | InvocationTargetException | MalformedURLException e) { throw new RuntimeException(e); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocation.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocation.java index ed248ba26..ae8dfe798 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocation.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocation.java @@ -27,6 +27,7 @@ package me.lucko.luckperms.common.dependencies.relocation; import java.util.Arrays; import java.util.List; +import java.util.Objects; public final class Relocation { private static final String RELOCATION_PREFIX = "me.lucko.luckperms.lib."; @@ -55,4 +56,17 @@ public final class Relocation { return this.relocatedPattern; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Relocation that = (Relocation) o; + return Objects.equals(this.pattern, that.pattern) && + Objects.equals(this.relocatedPattern, that.relocatedPattern); + } + + @Override + public int hashCode() { + return Objects.hash(this.pattern, this.relocatedPattern); + } } diff --git a/settings.gradle b/settings.gradle index e96ffbd5c..3d7d00337 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,14 @@ rootProject.name = 'luckperms' -include 'api', 'common', 'bukkit', 'bukkit-legacy', 'bungee', 'sponge', 'sponge:sponge-service', 'sponge:sponge-service-api6', 'sponge:sponge-service-api7', 'nukkit' +include ( + 'api', + 'common', + 'bukkit', + 'bukkit-legacy', + 'bungee', + 'sponge', 'sponge:sponge-service', 'sponge:sponge-service-api6', 'sponge:sponge-service-api7', + 'nukkit', + 'velocity' +) 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 e07056fea..1a85928b7 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java @@ -202,10 +202,6 @@ public class LPSpongeBootstrap implements LuckPermsBootstrap { return this.spongeScheduler; } - public Path getConfigPath() { - return this.configDirectory; - } - public PluginContainer getPluginContainer() { return this.pluginContainer; } 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 927af2ca8..91cb82778 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java @@ -222,10 +222,10 @@ public class LPSpongePlugin extends AbstractLuckPermsPlugin { } private Path resolveConfig() { - Path path = this.bootstrap.getConfigPath().resolve("luckperms.conf"); + Path path = this.bootstrap.getConfigDirectory().resolve("luckperms.conf"); if (!Files.exists(path)) { try { - MoreFiles.createDirectoriesIfNotExists(this.bootstrap.getConfigPath()); + MoreFiles.createDirectoriesIfNotExists(this.bootstrap.getConfigDirectory()); try (InputStream is = getClass().getClassLoader().getResourceAsStream("luckperms.conf")) { Files.copy(is, path); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeConfigAdapter.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeConfigAdapter.java index b9e12124a..8ff341d77 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeConfigAdapter.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeConfigAdapter.java @@ -25,98 +25,23 @@ package me.lucko.luckperms.sponge; -import com.google.common.base.Splitter; - -import me.lucko.luckperms.common.config.adapter.AbstractConfigurationAdapter; +import me.lucko.luckperms.common.config.adapter.ConfigurateConfigAdapter; import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import ninja.leaping.configurate.ConfigurationNode; -import ninja.leaping.configurate.commented.CommentedConfigurationNode; import ninja.leaping.configurate.hocon.HoconConfigurationLoader; import ninja.leaping.configurate.loader.ConfigurationLoader; -import java.io.IOException; import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -public class SpongeConfigAdapter extends AbstractConfigurationAdapter implements ConfigurationAdapter { - - private final Path path; - private ConfigurationNode root; +public class SpongeConfigAdapter extends ConfigurateConfigAdapter implements ConfigurationAdapter { public SpongeConfigAdapter(LuckPermsPlugin plugin, Path path) { - super(plugin); - this.path = path; - reload(); + super(plugin, path); } @Override - public void reload() { - ConfigurationLoader loader = HoconConfigurationLoader.builder().setPath(this.path).build(); - - try { - this.root = loader.load(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private ConfigurationNode resolvePath(String path) { - if (this.root == null) { - throw new RuntimeException("Config is not loaded."); - } - - return this.root.getNode(Splitter.on('.').splitToList(path).toArray()); - } - - @Override - public String getString(String path, String def) { - return resolvePath(path).getString(def); - } - - @Override - public int getInt(String path, int def) { - return resolvePath(path).getInt(def); - } - - @Override - public boolean getBoolean(String path, boolean def) { - return resolvePath(path).getBoolean(def); - } - - @Override - public List getList(String path, List def) { - ConfigurationNode node = resolvePath(path); - if (node.isVirtual()) { - return def; - } - - return node.getList(Object::toString); - } - - @Override - public List getKeys(String path, List def) { - ConfigurationNode node = resolvePath(path); - if (node.isVirtual()) { - return def; - } - - return node.getChildrenMap().keySet().stream().map(Object::toString).collect(Collectors.toList()); - } - - @SuppressWarnings("unchecked") - @Override - public Map getMap(String path, Map def) { - ConfigurationNode node = resolvePath(path); - if (node.isVirtual()) { - return def; - } - - Map m = (Map) node.getValue(Collections.emptyMap()); - return m.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue().toString())); + protected ConfigurationLoader createLoader(Path path) { + return HoconConfigurationLoader.builder().setPath(path).build(); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/BungeeMessenger.java b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java similarity index 95% rename from sponge/src/main/java/me/lucko/luckperms/sponge/messaging/BungeeMessenger.java rename to sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java index b441bb177..d1d15d6e0 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/BungeeMessenger.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/PluginMessageMessenger.java @@ -47,7 +47,7 @@ import javax.annotation.Nonnull; /** * An implementation of {@link Messenger} using the plugin messaging channels. */ -public class BungeeMessenger implements Messenger, RawDataListener { +public class PluginMessageMessenger implements Messenger, RawDataListener { private static final String CHANNEL = "luckperms:update"; private final LPSpongePlugin plugin; @@ -55,7 +55,7 @@ public class BungeeMessenger implements Messenger, RawDataListener { private ChannelBinding.RawDataChannel channel = null; - public BungeeMessenger(LPSpongePlugin plugin, IncomingMessageConsumer consumer) { + public PluginMessageMessenger(LPSpongePlugin plugin, IncomingMessageConsumer consumer) { this.plugin = plugin; this.consumer = consumer; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/SpongeMessagingFactory.java b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/SpongeMessagingFactory.java index cd47a80d9..514d6fbdc 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/SpongeMessagingFactory.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/SpongeMessagingFactory.java @@ -42,9 +42,9 @@ public class SpongeMessagingFactory extends MessagingFactory { @Override protected InternalMessagingService getServiceFor(String messagingType) { - if (messagingType.equals("bungee")) { + if (messagingType.equals("pluginmsg") || messagingType.equals("bungee") || messagingType.equals("velocity")) { try { - return new LuckPermsMessagingService(getPlugin(), new BungeeMessengerProvider()); + return new LuckPermsMessagingService(getPlugin(), new PluginMessageMessengerProvider()); } catch (Exception e) { e.printStackTrace(); } @@ -53,20 +53,20 @@ public class SpongeMessagingFactory extends MessagingFactory { return super.getServiceFor(messagingType); } - private class BungeeMessengerProvider implements MessengerProvider { + private class PluginMessageMessengerProvider implements MessengerProvider { @Nonnull @Override public String getName() { - return "Bungee"; + return "PluginMessage"; } @Nonnull @Override public Messenger obtain(@Nonnull IncomingMessageConsumer incomingMessageConsumer) { - BungeeMessenger bungeeMessaging = new BungeeMessenger(getPlugin(), incomingMessageConsumer); - bungeeMessaging.init(); - return bungeeMessaging; + PluginMessageMessenger messenger = new PluginMessageMessenger(getPlugin(), incomingMessageConsumer); + messenger.init(); + return messenger; } } diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 0cff25f88..af54213ce 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -210,9 +210,9 @@ watch-files = true # => 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 'none' and SQL storage is in use. Set to 'notsql' to disable this. -# => bungee Uses the plugin messaging channels to communicate with the proxy. +# => 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 BungeeCord proxy. +# 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. # => none Disables the service. diff --git a/velocity/build.gradle b/velocity/build.gradle new file mode 100644 index 000000000..e44e89141 --- /dev/null +++ b/velocity/build.gradle @@ -0,0 +1,54 @@ +buildscript { + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "com.github.jengelman.gradle.plugins:shadow:2.0.4" + classpath "gradle.plugin.ninja.miserable:blossom:1.0.1" + } +} + +apply plugin: "com.github.johnrengelman.shadow" +apply plugin: "ninja.miserable.blossom" + +dependencies { + compile project(':common') + + compileOnly 'com.velocitypowered:velocity-api:1.0-SNAPSHOT' +} + +blossom { + replaceTokenIn('src/main/java/me/lucko/luckperms/velocity/LPVelocityBootstrap.java') + replaceToken '@version@', project.ext.fullVersion +} + +shadowJar { + archiveName = "LuckPerms-Velocity-${project.ext.fullVersion}.jar" + + dependencies { + include(dependency('me.lucko.luckperms:.*')) + } + + // relocate 'net.kyori.text', 'me.lucko.luckperms.lib.text' (included in velocity!) + 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 '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 'org.apache.commons.pool2', 'me.lucko.luckperms.lib.commonspool2' + relocate 'ninja.leaping.configurate', 'me.lucko.luckperms.lib.configurate' + relocate 'org.yaml.snakeyaml', 'me.lucko.luckperms.lib.yaml' +} + +artifacts { + archives shadowJar +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityBootstrap.java b/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityBootstrap.java new file mode 100644 index 000000000..9983b8205 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityBootstrap.java @@ -0,0 +1,241 @@ +/* + * 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.velocity; + +import com.google.inject.Inject; +import com.velocitypowered.api.event.PostOrder; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; +import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; + +import me.lucko.luckperms.api.platform.PlatformType; +import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader; +import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; +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 org.slf4j.Logger; + +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.stream.Stream; + +/** + * Bootstrap plugin for LuckPerms running on Velocity. + */ +@Plugin( + id = "luckperms", + name = "LuckPerms", + version = "@version@", + authors = "Luck", + description = "A permissions plugin", + url = "https://github.com/lucko/LuckPerms" +) +public class LPVelocityBootstrap implements LuckPermsBootstrap { + + /** + * The plugin logger + */ + private final PluginLogger logger; + + /** + * A scheduler adapter for the platform + */ + private final SchedulerAdapter schedulerAdapter; + + /** + * The plugin classloader + */ + private final PluginClassLoader classLoader; + + /** + * The plugin instance + */ + private final LPVelocityPlugin plugin; + + /** + * The time when the plugin was enabled + */ + private long startTime; + + // load/enable latches + private final CountDownLatch loadLatch = new CountDownLatch(1); + private final CountDownLatch enableLatch = new CountDownLatch(1); + + @Inject + private ProxyServer proxy; + + @Inject + @DataDirectory + private Path configDirectory; + + @Inject + public LPVelocityBootstrap(Logger logger) { + this.logger = new Slf4jPluginLogger(logger); + this.schedulerAdapter = new VelocitySchedulerAdapter(this); + this.classLoader = new VelocityClassLoader(this); + this.plugin = new LPVelocityPlugin(this); + } + + // provide adapters + + @Override + public PluginLogger getPluginLogger() { + return this.logger; + } + + @Override + public SchedulerAdapter getScheduler() { + return this.schedulerAdapter; + } + + @Override + public PluginClassLoader getPluginClassLoader() { + return this.classLoader; + } + + // lifecycle + + @Subscribe(order = PostOrder.FIRST) + public void onEnable(ProxyInitializeEvent e) { + this.startTime = System.currentTimeMillis(); + try { + this.plugin.load(); + } finally { + this.loadLatch.countDown(); + } + + try { + this.plugin.enable(); + } finally { + this.enableLatch.countDown(); + } + } + + @Subscribe(order = PostOrder.LAST) + public void onDisable(ProxyShutdownEvent e) { + this.plugin.disable(); + } + + @Override + public CountDownLatch getEnableLatch() { + return this.enableLatch; + } + + @Override + public CountDownLatch getLoadLatch() { + return this.loadLatch; + } + + // getters for the injected sponge instances + + public ProxyServer getProxy() { + return this.proxy; + } + + // provide information about the plugin + + @Override + public String getVersion() { + return "@version@"; + } + + @Override + public long getStartupTime() { + return this.startTime; + } + + // provide information about the platform + + @Override + public PlatformType getType() { + return PlatformType.VELOCITY; + } + + @Override + public String getServerBrand() { + return "Velocity"; + } + + @Override + public String getServerVersion() { + return ProxyServer.class.getPackage().getImplementationVersion(); + } + + @Override + public Path getDataDirectory() { + return this.configDirectory.toAbsolutePath(); + } + + @Override + public InputStream getResourceStream(String path) { + return getClass().getClassLoader().getResourceAsStream(path); + } + + @Override + public Optional getPlayer(UUID uuid) { + return this.proxy.getPlayer(uuid); + } + + @Override + public Optional lookupUuid(String username) { + return Optional.empty(); + } + + @Override + public Optional lookupUsername(UUID uuid) { + return Optional.empty(); + } + + @Override + public int getPlayerCount() { + return this.proxy.getPlayerCount(); + } + + @Override + public Stream getPlayerList() { + return this.proxy.getAllPlayers().stream().map(Player::getUsername); + } + + @Override + public Stream getOnlinePlayers() { + return this.proxy.getAllPlayers().stream().map(Player::getUniqueId); + } + + @Override + public boolean isPlayerOnline(UUID uuid) { + Player player = this.proxy.getPlayer(uuid).orElse(null); + return player != null && player.isActive(); + } +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityPlugin.java b/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityPlugin.java new file mode 100644 index 000000000..9837c0984 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityPlugin.java @@ -0,0 +1,236 @@ +/* + * 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.velocity; + +import com.velocitypowered.api.proxy.Player; + +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.api.LuckPermsApi; +import me.lucko.luckperms.common.api.LuckPermsApiProvider; +import me.lucko.luckperms.common.calculators.CalculatorFactory; +import me.lucko.luckperms.common.command.CommandManager; +import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter; +import me.lucko.luckperms.common.contexts.ContextManager; +import me.lucko.luckperms.common.dependencies.Dependency; +import me.lucko.luckperms.common.event.AbstractEventBus; +import me.lucko.luckperms.common.managers.group.StandardGroupManager; +import me.lucko.luckperms.common.managers.track.StandardTrackManager; +import me.lucko.luckperms.common.managers.user.StandardUserManager; +import me.lucko.luckperms.common.messaging.MessagingFactory; +import me.lucko.luckperms.common.model.User; +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.common.utils.MoreFiles; +import me.lucko.luckperms.velocity.calculators.VelocityCalculatorFactory; +import me.lucko.luckperms.velocity.contexts.BackendServerCalculator; +import me.lucko.luckperms.velocity.contexts.VelocityContextManager; +import me.lucko.luckperms.velocity.listeners.MonitoringPermissionCheckListener; +import me.lucko.luckperms.velocity.listeners.VelocityConnectionListener; +import me.lucko.luckperms.velocity.messaging.VelocityMessagingFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.EnumSet; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +/** + * LuckPerms implementation for the Velocity API. + */ +public class LPVelocityPlugin extends AbstractLuckPermsPlugin { + private final LPVelocityBootstrap bootstrap; + + private VelocitySenderFactory senderFactory; + private VelocityConnectionListener connectionListener; + private CommandManager commandManager; + private StandardUserManager userManager; + private StandardGroupManager groupManager; + private StandardTrackManager trackManager; + private ContextManager contextManager; + + public LPVelocityPlugin(LPVelocityBootstrap bootstrap) { + this.bootstrap = bootstrap; + } + + @Override + public LPVelocityBootstrap getBootstrap() { + return this.bootstrap; + } + + @Override + protected void setupSenderFactory() { + this.senderFactory = new VelocitySenderFactory(this); + } + + @Override + protected Set getGlobalDependencies() { + return EnumSet.of(Dependency.CAFFEINE, Dependency.OKIO, Dependency.OKHTTP, Dependency.EVENT, + Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_YAML, Dependency.SNAKEYAML); + } + + @Override + protected ConfigurationAdapter provideConfigurationAdapter() { + return new VelocityConfigAdapter(this, resolveConfig()); + } + + @Override + protected void registerPlatformListeners() { + this.connectionListener = new VelocityConnectionListener(this); + this.bootstrap.getProxy().getEventManager().register(this.bootstrap, this.connectionListener); + this.bootstrap.getProxy().getEventManager().register(this.bootstrap, new MonitoringPermissionCheckListener(this)); + } + + @Override + protected MessagingFactory provideMessagingFactory() { + return new VelocityMessagingFactory(this); + } + + @Override + protected void registerCommands() { + this.commandManager = new CommandManager(this); + this.bootstrap.getProxy().getCommandManager().register(new VelocityCommandExecutor(this, this.commandManager), "luckpermsvelocity", "lpv", "vperm", "vperms", "vpermission", "vpermissions"); + } + + @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 VelocityCalculatorFactory(this); + } + + @Override + protected void setupContextManager() { + this.contextManager = new VelocityContextManager(this); + this.contextManager.registerCalculator(new BackendServerCalculator(this)); + } + + @Override + protected void setupPlatformHooks() { + + } + + @Override + protected AbstractEventBus provideEventBus(LuckPermsApiProvider apiProvider) { + return new VelocityEventBus(this, apiProvider); + } + + @Override + protected void registerApiOnPlatform(LuckPermsApi 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() { + + } + + private Path resolveConfig() { + Path path = this.bootstrap.getConfigDirectory().resolve("config.yml"); + if (!Files.exists(path)) { + try { + MoreFiles.createDirectoriesIfNotExists(this.bootstrap.getConfigDirectory()); + try (InputStream is = getClass().getClassLoader().getResourceAsStream("config.yml")) { + Files.copy(is, path); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return path; + } + + @Override + public Optional getContextForUser(User user) { + return this.bootstrap.getPlayer(user.getUuid()).map(player -> this.contextManager.getApplicableContexts(player)); + } + + @Override + public Stream getOnlineSenders() { + return Stream.concat( + Stream.of(getConsoleSender()), + this.bootstrap.getProxy().getAllPlayers().stream().map(p -> this.senderFactory.wrap(p)) + ); + } + + @Override + public Sender getConsoleSender() { + return this.senderFactory.wrap(this.bootstrap.getProxy().getConsoleCommandSource()); + } + + public VelocitySenderFactory getSenderFactory() { + return this.senderFactory; + } + + @Override + public AbstractConnectionListener getConnectionListener() { + return this.connectionListener; + } + + @Override + public CommandManager 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 ContextManager getContextManager() { + return this.contextManager; + } + +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityClassLoader.java b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityClassLoader.java new file mode 100644 index 000000000..4c16bcfe7 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityClassLoader.java @@ -0,0 +1,18 @@ +package me.lucko.luckperms.velocity; + +import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader; + +import java.nio.file.Path; + +public class VelocityClassLoader implements PluginClassLoader { + private final LPVelocityBootstrap bootstrap; + + public VelocityClassLoader(LPVelocityBootstrap bootstrap) { + this.bootstrap = bootstrap; + } + + @Override + public void loadJar(Path file) { + this.bootstrap.getProxy().getPluginManager().addToClasspath(bootstrap, file); + } +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityCommandExecutor.java b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityCommandExecutor.java new file mode 100644 index 000000000..6f87fe438 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityCommandExecutor.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.velocity; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.velocitypowered.api.command.Command; +import com.velocitypowered.api.command.CommandSource; + +import me.lucko.luckperms.common.command.CommandManager; +import me.lucko.luckperms.common.sender.Sender; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.List; + +public class VelocityCommandExecutor implements Command { + private static final Splitter TAB_COMPLETE_ARGUMENT_SPLITTER = Splitter.on(CommandManager.COMMAND_SEPARATOR_PATTERN); + private static final Splitter ARGUMENT_SPLITTER = Splitter.on(CommandManager.COMMAND_SEPARATOR_PATTERN).omitEmptyStrings(); + private static final Joiner ARGUMENT_JOINER = Joiner.on(' '); + + private final LPVelocityPlugin plugin; + private final CommandManager manager; + + public VelocityCommandExecutor(LPVelocityPlugin plugin, CommandManager manager) { + this.plugin = plugin; + this.manager = manager; + } + + @Override + public void execute(@NonNull CommandSource source, String[] args) { + Sender lpSender = this.plugin.getSenderFactory().wrap(source); + List arguments = CommandManager.stripQuotes(ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args))); + + this.manager.onCommand(lpSender, "lpv", arguments); + } + + @Override + public List suggest(@NonNull CommandSource source, String[] args) { + Sender lpSender = this.plugin.getSenderFactory().wrap(source); + List arguments = CommandManager.stripQuotes(TAB_COMPLETE_ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args))); + + return this.manager.onTabComplete(lpSender, arguments); + } +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityConfigAdapter.java b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityConfigAdapter.java new file mode 100644 index 000000000..174b10b04 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityConfigAdapter.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.velocity; + +import me.lucko.luckperms.common.config.adapter.ConfigurateConfigAdapter; +import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.loader.ConfigurationLoader; +import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; + +import java.nio.file.Path; + +public class VelocityConfigAdapter extends ConfigurateConfigAdapter implements ConfigurationAdapter { + public VelocityConfigAdapter(LuckPermsPlugin plugin, Path path) { + super(plugin, path); + } + + @Override + protected ConfigurationLoader createLoader(Path path) { + return YAMLConfigurationLoader.builder().setPath(path).build(); + } +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityEventBus.java b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityEventBus.java new file mode 100644 index 000000000..2393a41d3 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocityEventBus.java @@ -0,0 +1,55 @@ +/* + * 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.velocity; + +import com.velocitypowered.api.plugin.PluginContainer; + +import me.lucko.luckperms.common.api.LuckPermsApiProvider; +import me.lucko.luckperms.common.event.AbstractEventBus; + +public class VelocityEventBus extends AbstractEventBus { + private final LPVelocityPlugin plugin; + + public VelocityEventBus(LPVelocityPlugin plugin, LuckPermsApiProvider apiProvider) { + super(plugin, apiProvider); + this.plugin = plugin; + } + + @Override + protected PluginContainer checkPlugin(Object plugin) throws IllegalArgumentException { + if (plugin instanceof PluginContainer) { + return (PluginContainer) plugin; + } + + PluginContainer pluginContainer = this.plugin.getBootstrap().getProxy().getPluginManager().fromInstance(plugin).orElse(null); + if (pluginContainer != null) { + return pluginContainer; + } + + throw new IllegalArgumentException("Object " + plugin + " (" + plugin.getClass().getName() + ") is not a plugin."); + } + +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/VelocitySchedulerAdapter.java b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocitySchedulerAdapter.java new file mode 100644 index 000000000..f64b410fb --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocitySchedulerAdapter.java @@ -0,0 +1,87 @@ +/* + * 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.velocity; + +import com.velocitypowered.api.scheduler.ScheduledTask; + +import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; +import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask; +import me.lucko.luckperms.common.utils.Iterators; + +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +public class VelocitySchedulerAdapter implements SchedulerAdapter { + private final LPVelocityBootstrap bootstrap; + + private final Executor executor; + private final Set tasks = Collections.newSetFromMap(new WeakHashMap<>()); + + public VelocitySchedulerAdapter(LPVelocityBootstrap bootstrap) { + this.bootstrap = bootstrap; + this.executor = r -> bootstrap.getProxy().getScheduler().buildTask(bootstrap, r).schedule(); + } + + @Override + public Executor async() { + return this.executor; + } + + @Override + public Executor sync() { + return this.executor; + } + + @Override + public SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit) { + ScheduledTask t = this.bootstrap.getProxy().getScheduler().buildTask(this.bootstrap, task) + .delay((int) delay, unit) + .schedule(); + + this.tasks.add(t); + return t::cancel; + } + + @Override + public SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit) { + ScheduledTask t = this.bootstrap.getProxy().getScheduler().buildTask(this.bootstrap, task) + .delay((int) interval, unit) + .repeat((int) interval, unit) + .schedule(); + + this.tasks.add(t); + return t::cancel; + } + + @Override + public void shutdown() { + Iterators.iterate(this.tasks, ScheduledTask::cancel); + } + +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/VelocitySenderFactory.java b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocitySenderFactory.java new file mode 100644 index 000000000..3c9925b8b --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/VelocitySenderFactory.java @@ -0,0 +1,81 @@ +/* + * 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.velocity; + +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.Player; + +import me.lucko.luckperms.api.Tristate; +import me.lucko.luckperms.common.command.CommandManager; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.sender.SenderFactory; +import me.lucko.luckperms.common.utils.TextUtils; + +import net.kyori.text.Component; + +import java.util.UUID; + +public class VelocitySenderFactory extends SenderFactory { + public VelocitySenderFactory(LuckPermsPlugin plugin) { + super(plugin); + } + + @Override + protected String getName(CommandSource source) { + if (source instanceof Player) { + return ((Player) source).getUsername(); + } + return CommandManager.CONSOLE_NAME; + } + + @Override + protected UUID getUuid(CommandSource source) { + if (source instanceof Player) { + return ((Player) source).getUniqueId(); + } + return CommandManager.CONSOLE_UUID; + } + + @Override + protected void sendMessage(CommandSource source, String s) { + sendMessage(source, TextUtils.fromLegacy(s)); + } + + @Override + protected void sendMessage(CommandSource source, Component message) { + source.sendMessage(message); + } + + @Override + protected Tristate getPermissionValue(CommandSource source, String node) { + return Tristate.fromBoolean(hasPermission(source, node)); + } + + @Override + protected boolean hasPermission(CommandSource source, String node) { + return source.hasPermission(node); + } +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/calculators/VelocityCalculatorFactory.java b/velocity/src/main/java/me/lucko/luckperms/velocity/calculators/VelocityCalculatorFactory.java new file mode 100644 index 000000000..f0da91b4b --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/calculators/VelocityCalculatorFactory.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.velocity.calculators; + +import com.google.common.collect.ImmutableList; + +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.velocity.LPVelocityPlugin; +import me.lucko.luckperms.common.calculators.CalculatorFactory; +import me.lucko.luckperms.common.calculators.PermissionCalculator; +import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata; +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.processors.MapProcessor; +import me.lucko.luckperms.common.processors.PermissionProcessor; +import me.lucko.luckperms.common.processors.RegexProcessor; +import me.lucko.luckperms.common.processors.WildcardProcessor; + +public class VelocityCalculatorFactory implements CalculatorFactory { + private final LPVelocityPlugin plugin; + + public VelocityCalculatorFactory(LPVelocityPlugin plugin) { + this.plugin = plugin; + } + + @Override + public PermissionCalculator build(Contexts contexts, PermissionCalculatorMetadata metadata) { + ImmutableList.Builder processors = ImmutableList.builder(); + + processors.add(new MapProcessor()); + + if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) { + processors.add(new RegexProcessor()); + } + + if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) { + processors.add(new WildcardProcessor()); + } + + return new PermissionCalculator(this.plugin, metadata, processors.build()); + } +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/contexts/BackendServerCalculator.java b/velocity/src/main/java/me/lucko/luckperms/velocity/contexts/BackendServerCalculator.java new file mode 100644 index 000000000..ff1b1984f --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/contexts/BackendServerCalculator.java @@ -0,0 +1,61 @@ +/* + * 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.velocity.contexts; + +import com.velocitypowered.api.proxy.Player; + +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.api.context.ContextCalculator; +import me.lucko.luckperms.api.context.MutableContextSet; +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import javax.annotation.Nonnull; + +public class BackendServerCalculator implements ContextCalculator { + + private static String getServer(Player player) { + return player.getCurrentServer().isPresent() ? player.getCurrentServer().get().getServerInfo().getName().toLowerCase() : null; + } + + private final LuckPermsPlugin plugin; + + public BackendServerCalculator(LuckPermsPlugin plugin) { + this.plugin = plugin; + } + + @Nonnull + @Override + public MutableContextSet giveApplicableContext(@Nonnull Player subject, @Nonnull MutableContextSet accumulator) { + String server = getServer(subject); + while (server != null && !accumulator.has(Contexts.WORLD_KEY, server)) { + accumulator.add(Contexts.WORLD_KEY, server); + server = this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).getOrDefault(server, server).toLowerCase(); + } + + return accumulator; + } +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/contexts/VelocityContextManager.java b/velocity/src/main/java/me/lucko/luckperms/velocity/contexts/VelocityContextManager.java new file mode 100644 index 000000000..f88df8314 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/contexts/VelocityContextManager.java @@ -0,0 +1,76 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.velocity.contexts; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.velocitypowered.api.proxy.Player; + +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.api.context.ImmutableContextSet; +import me.lucko.luckperms.common.contexts.ContextManager; +import me.lucko.luckperms.common.contexts.ContextsCache; +import me.lucko.luckperms.common.contexts.ContextsSupplier; +import me.lucko.luckperms.velocity.LPVelocityPlugin; + +import java.util.concurrent.TimeUnit; + +public class VelocityContextManager extends ContextManager { + + private final LoadingCache> subjectCaches = Caffeine.newBuilder() + .expireAfterAccess(1, TimeUnit.MINUTES) + .build(key -> new ContextsCache<>(key, this)); + + public VelocityContextManager(LPVelocityPlugin plugin) { + super(plugin, Player.class); + } + + @Override + public ContextsSupplier getCacheFor(Player subject) { + if (subject == null) { + throw new NullPointerException("subject"); + } + + return subjectCaches.get(subject); + } + + @Override + public void invalidateCache(Player subject) { + if (subject == null) { + throw new NullPointerException("subject"); + } + + ContextsCache cache = this.subjectCaches.getIfPresent(subject); + if (cache != null) { + cache.invalidate(); + } + } + + @Override + public Contexts formContexts(Player subject, ImmutableContextSet contextSet) { + return formContexts(contextSet); + } +} 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 new file mode 100644 index 000000000..6ea6e02e4 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/MonitoringPermissionCheckListener.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.velocity.listeners; + +import com.velocitypowered.api.event.PostOrder; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.permission.PermissionsSetupEvent; +import com.velocitypowered.api.permission.PermissionFunction; +import com.velocitypowered.api.permission.PermissionProvider; +import com.velocitypowered.api.permission.PermissionSubject; +import com.velocitypowered.api.proxy.Player; + +import me.lucko.luckperms.api.Tristate; +import me.lucko.luckperms.api.context.ContextSet; +import me.lucko.luckperms.common.verbose.CheckOrigin; +import me.lucko.luckperms.velocity.LPVelocityPlugin; +import me.lucko.luckperms.velocity.service.CompatibilityUtil; + +import org.checkerframework.checker.nullness.qual.NonNull; + +public class MonitoringPermissionCheckListener { + private final LPVelocityPlugin plugin; + + public MonitoringPermissionCheckListener(LPVelocityPlugin plugin) { + this.plugin = plugin; + } + + @Subscribe(order = PostOrder.LAST) + public void onOtherPermissionSetup(PermissionsSetupEvent e) { + // players are handled separately + if (e.getSubject() instanceof Player) { + return; + } + + e.setProvider(new MonitoredPermissionProvider(e.getProvider())); + } + + private final class MonitoredPermissionProvider implements PermissionProvider { + private final PermissionProvider delegate; + + MonitoredPermissionProvider(PermissionProvider delegate) { + this.delegate = delegate; + } + + @Override + public @NonNull PermissionFunction createFunction(@NonNull PermissionSubject subject) { + PermissionFunction function = this.delegate.createFunction(subject); + return new MonitoredPermissionFunction(subject, function); + } + } + + private final class MonitoredPermissionFunction implements PermissionFunction { + private final String name; + private final PermissionFunction delegate; + + MonitoredPermissionFunction(PermissionSubject subject, PermissionFunction delegate) { + this.delegate = delegate; + this.name = determineName(subject); + } + + @Override + public com.velocitypowered.api.permission.@NonNull Tristate getPermissionSetting(@NonNull String permission) { + com.velocitypowered.api.permission.Tristate setting = this.delegate.getPermissionSetting(permission); + + // report result + Tristate result = CompatibilityUtil.convertTristate(setting); + String name = "internal/" + this.name; + + MonitoringPermissionCheckListener.this.plugin.getVerboseHandler().offerCheckData(CheckOrigin.PLATFORM_LOOKUP_CHECK, name, ContextSet.empty(), permission, result); + MonitoringPermissionCheckListener.this.plugin.getPermissionRegistry().offer(permission); + + return setting; + } + } + + private String determineName(PermissionSubject subject) { + if (subject == this.plugin.getBootstrap().getProxy().getConsoleCommandSource()) { + return "console"; + } + return subject.getClass().getSimpleName(); + } +} 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 new file mode 100644 index 000000000..f833108db --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/listeners/VelocityConnectionListener.java @@ -0,0 +1,169 @@ +/* + * 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.velocity.listeners; + +import com.velocitypowered.api.event.PostOrder; +import com.velocitypowered.api.event.ResultedEvent; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.event.connection.LoginEvent; +import com.velocitypowered.api.event.permission.PermissionsSetupEvent; +import com.velocitypowered.api.proxy.Player; + +import me.lucko.luckperms.velocity.LPVelocityPlugin; +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.locale.message.Message; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener; +import me.lucko.luckperms.velocity.service.PlayerPermissionProvider; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class VelocityConnectionListener extends AbstractConnectionListener { + private final LPVelocityPlugin plugin; + + private final Set deniedLogin = Collections.synchronizedSet(new HashSet<>()); + + public VelocityConnectionListener(LPVelocityPlugin plugin) { + super(plugin); + this.plugin = plugin; + } + + @Subscribe + public void onPlayerPermissionsSetup(PermissionsSetupEvent e) { + /* Called when the player first attempts a connection with the server. + The PermissionsSetupEvent is called for players just before the Login event + + We delay the login here, as we want to cache UUID data before the player is connected to a backend bukkit server. + This means that a player will have the same UUID across the network, even if parts of the network are running in + Offline mode. */ + + if (!(e.getSubject() instanceof Player)) { + return; + } + + final Player p = (Player) e.getSubject(); + + if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { + 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()); + this.plugin.getEventFactory().handleUserLoginProcess(p.getUniqueId(), p.getUsername(), user); + recordConnection(p.getUniqueId()); + + // set permission provider + e.setProvider(new PlayerPermissionProvider(p, user, this.plugin.getContextManager().getCacheFor(p))); + } catch (Exception ex) { + this.plugin.getLogger().severe("Exception occurred whilst loading data for " + p.getUniqueId() + " - " + p.getUsername()); + ex.printStackTrace(); + + // there was some error loading + if (this.plugin.getConfiguration().get(ConfigKeys.CANCEL_FAILED_LOGINS)) { + // cancel the login attempt + this.deniedLogin.add(p.getUniqueId()); + } + } + } + + @Subscribe(order = PostOrder.FIRST) + public void onPlayerLogin(LoginEvent e) { + if (this.deniedLogin.remove(e.getPlayer().getUniqueId())) { + e.setResult(ResultedEvent.ComponentResult.denied(Message.LOADING_DATABASE_ERROR.asComponent(this.plugin.getLocaleManager()))); + } + } + + @Subscribe + public void onPlayerPostLogin(LoginEvent e) { + final Player player = e.getPlayer(); + final User user = this.plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()); + + if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { + this.plugin.getLogger().info("Processing post-login for " + player.getUniqueId() + " - " + player.getUsername()); + } + + if (!e.getResult().isAllowed()) { + return; + } + + if (user == null) { + if (!getUniqueConnections().contains(player.getUniqueId())) { + this.plugin.getLogger().warn("User " + player.getUniqueId() + " - " + player.getUsername() + + " doesn't have data pre-loaded, they have never need processed during pre-login in this session."); + } else { + this.plugin.getLogger().warn("User " + player.getUniqueId() + " - " + player.getUsername() + + " doesn't currently have data pre-loaded, but they have been processed before in this session."); + } + + if (this.plugin.getConfiguration().get(ConfigKeys.CANCEL_FAILED_LOGINS)) { + // disconnect the user + e.setResult(ResultedEvent.ComponentResult.denied(Message.LOADING_STATE_ERROR.asComponent(this.plugin.getLocaleManager()))); + } else { + // just send a message + this.plugin.getBootstrap().getScheduler().asyncLater(() -> { + if (!player.isActive()) { + return; + } + + player.sendMessage(Message.LOADING_STATE_ERROR.asComponent(this.plugin.getLocaleManager())); + }, 1, TimeUnit.SECONDS); + } + } + } + + // Wait until the last priority to unload, so plugins can still perform permission checks on this event + @Subscribe(order = PostOrder.LAST) + public void onPlayerQuit(DisconnectEvent e) { + Player player = e.getPlayer(); + + // Register with the housekeeper, so the User's instance will stick + // around for a bit after they disconnect + this.plugin.getUserManager().getHouseKeeper().registerUsage(player.getUniqueId()); + + // force a clear of transient nodes + this.plugin.getBootstrap().getScheduler().executeAsync(() -> { + User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId()); + if (user != null) { + user.clearTransientNodes(); + } + }); + } + +} 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 new file mode 100644 index 000000000..4e2f16ed8 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/messaging/PluginMessageMessenger.java @@ -0,0 +1,109 @@ +/* + * 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.velocity.messaging; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; +import com.velocitypowered.api.proxy.messages.ChannelMessageSource; +import com.velocitypowered.api.proxy.messages.ChannelRegistrar; +import com.velocitypowered.api.proxy.messages.ChannelSide; +import com.velocitypowered.api.proxy.messages.MessageHandler; +import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; + +import me.lucko.luckperms.api.messenger.IncomingMessageConsumer; +import me.lucko.luckperms.api.messenger.Messenger; +import me.lucko.luckperms.api.messenger.message.OutgoingMessage; +import me.lucko.luckperms.velocity.LPVelocityPlugin; + +import java.util.Optional; + +import javax.annotation.Nonnull; + +/** + * An implementation of {@link Messenger} using the plugin messaging channels. + */ +public class PluginMessageMessenger implements Messenger, MessageHandler { + private static final ChannelIdentifier CHANNEL = MinecraftChannelIdentifier.create("luckperms", "update"); + + private final LPVelocityPlugin plugin; + private final IncomingMessageConsumer consumer; + + public PluginMessageMessenger(LPVelocityPlugin plugin, IncomingMessageConsumer consumer) { + this.plugin = plugin; + this.consumer = consumer; + } + + public void init() { + ChannelRegistrar channelRegistrar = this.plugin.getBootstrap().getProxy().getChannelRegistrar(); + channelRegistrar.register(this, CHANNEL); + } + + @Override + public void close() { + // TODO: no way to unregister an individual MessageHandler? + } + + private void dispatchMessage(byte[] data) { + this.plugin.getBootstrap().getScheduler().executeAsync(() -> { + this.plugin.getBootstrap().getProxy().getAllPlayers().stream() + .map(Player::getCurrentServer) + .filter(Optional::isPresent) + .map(Optional::get) + .distinct() + .forEach(server -> server.sendPluginMessage(CHANNEL, data)); + }); + } + + @Override + public void sendOutgoingMessage(@Nonnull OutgoingMessage outgoingMessage) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF(outgoingMessage.asEncodedString()); + + byte[] data = out.toByteArray(); + dispatchMessage(data); + } + + @Override + public ForwardStatus handle(ChannelMessageSource source, ChannelSide side, ChannelIdentifier channel, byte[] data) { + if (side == ChannelSide.FROM_CLIENT) { + return ForwardStatus.HANDLED; + } + + ByteArrayDataInput in = ByteStreams.newDataInput(data); + String msg = in.readUTF(); + + if (this.consumer.consumeIncomingMessageAsString(msg)) { + // Forward to other servers + this.plugin.getBootstrap().getScheduler().executeAsync(() -> dispatchMessage(data)); + } + + return ForwardStatus.HANDLED; + } + +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/messaging/VelocityMessagingFactory.java b/velocity/src/main/java/me/lucko/luckperms/velocity/messaging/VelocityMessagingFactory.java new file mode 100644 index 000000000..23fc38cf9 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/messaging/VelocityMessagingFactory.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.velocity.messaging; + +import me.lucko.luckperms.api.messenger.IncomingMessageConsumer; +import me.lucko.luckperms.api.messenger.Messenger; +import me.lucko.luckperms.api.messenger.MessengerProvider; +import me.lucko.luckperms.velocity.LPVelocityPlugin; +import me.lucko.luckperms.common.messaging.InternalMessagingService; +import me.lucko.luckperms.common.messaging.LuckPermsMessagingService; +import me.lucko.luckperms.common.messaging.MessagingFactory; + +import javax.annotation.Nonnull; + +public class VelocityMessagingFactory extends MessagingFactory { + public VelocityMessagingFactory(LPVelocityPlugin plugin) { + super(plugin); + } + + @Override + protected InternalMessagingService getServiceFor(String messagingType) { + if (messagingType.equals("pluginmsg") || messagingType.equals("velocity")) { + try { + return new LuckPermsMessagingService(getPlugin(), new PluginMessageMessengerProvider()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + return super.getServiceFor(messagingType); + } + + private class PluginMessageMessengerProvider implements MessengerProvider { + + @Nonnull + @Override + public String getName() { + return "PluginMessage"; + } + + @Nonnull + @Override + public Messenger obtain(@Nonnull IncomingMessageConsumer incomingMessageConsumer) { + PluginMessageMessenger messenger = new PluginMessageMessenger(getPlugin(), incomingMessageConsumer); + messenger.init(); + return messenger; + } + } +} diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/service/CompatibilityUtil.java b/velocity/src/main/java/me/lucko/luckperms/velocity/service/CompatibilityUtil.java new file mode 100644 index 000000000..7370e0fa8 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/service/CompatibilityUtil.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.velocity.service; + +import me.lucko.luckperms.api.Tristate; + +import java.util.Objects; + +/** + * Utility class for converting between Velocity and LuckPerms tristate classes + */ +public final class CompatibilityUtil { + + public static com.velocitypowered.api.permission.Tristate convertTristate(Tristate tristate) { + Objects.requireNonNull(tristate, "tristate"); + switch (tristate) { + case TRUE: + return com.velocitypowered.api.permission.Tristate.TRUE; + case FALSE: + return com.velocitypowered.api.permission.Tristate.FALSE; + default: + return com.velocitypowered.api.permission.Tristate.UNDEFINED; + } + } + + public static Tristate convertTristate(com.velocitypowered.api.permission.Tristate tristate) { + Objects.requireNonNull(tristate, "tristate"); + switch (tristate) { + case TRUE: + return Tristate.TRUE; + case FALSE: + return Tristate.FALSE; + default: + return Tristate.UNDEFINED; + } + } + + private CompatibilityUtil() {} + +} 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 new file mode 100644 index 000000000..caaa70b92 --- /dev/null +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/service/PlayerPermissionProvider.java @@ -0,0 +1,37 @@ +package me.lucko.luckperms.velocity.service; + +import com.google.common.base.Preconditions; +import com.velocitypowered.api.permission.PermissionFunction; +import com.velocitypowered.api.permission.PermissionProvider; +import com.velocitypowered.api.permission.PermissionSubject; +import com.velocitypowered.api.permission.Tristate; +import com.velocitypowered.api.proxy.Player; + +import me.lucko.luckperms.common.contexts.ContextsSupplier; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.verbose.CheckOrigin; + +import org.checkerframework.checker.nullness.qual.NonNull; + +public class PlayerPermissionProvider implements PermissionProvider, PermissionFunction { + private final Player player; + private final User user; + private final ContextsSupplier contextsSupplier; + + public PlayerPermissionProvider(Player player, User user, ContextsSupplier contextsSupplier) { + this.player = player; + this.user = user; + this.contextsSupplier = contextsSupplier; + } + + @Override + public @NonNull PermissionFunction createFunction(@NonNull PermissionSubject subject) { + Preconditions.checkState(subject == this.player, "createFunction called with different argument"); + return this; + } + + @Override + public @NonNull Tristate getPermissionSetting(@NonNull String permission) { + return CompatibilityUtil.convertTristate(this.user.getCachedData().getPermissionData(this.contextsSupplier.getContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK)); + } +} diff --git a/velocity/src/main/resources/config.yml b/velocity/src/main/resources/config.yml new file mode 100644 index 000000000..df2247318 --- /dev/null +++ b/velocity/src/main/resources/config.yml @@ -0,0 +1,531 @@ +#################################################################################################### +# +----------------------------------------------------------------------------------------------+ # +# | __ __ ___ __ __ | # +# | | | | / ` |__/ |__) |__ |__) |\/| /__` | # +# | |___ \__/ \__, | \ | |___ | \ | | .__/ | # +# | | # +# | | # +# | SOURCE CODE: https://github.com/lucko/LuckPerms | # +# | WIKI: https://github.com/lucko/LuckPerms/wiki | # +# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | # +# | | # +# | Each option in this file is documented and explained here: | # +# | ==> https://github.com/lucko/LuckPerms/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://github.com/lucko/LuckPerms/wiki/Context +server: proxy + + + + +# +----------------------------------------------------------------------------------------------+ # +# | | # +# | 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://github.com/lucko/LuckPerms/wiki/Choosing-a-Storage-type +# +# - 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 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. + properties: + useUnicode: true + characterEncoding: utf8 + + # The prefix for all LuckPerms SQL tables. + # - Change this is 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. + mongodb_collection_prefix: '' + + # MongoDB ClientConnectionURI for use with replica sets and custom connection options + # - 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 'none' 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 Velocity proxy. +# => redis Uses Redis pub-sub to push changes. Your server connection info must be +# configured below. +# => none Disables the service. +messaging-service: none + +# 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 Velocity 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: false + +# Settings for Redis. +# Port 6379 is used by default; set address to "host:port" if differs +redis: + enabled: false + address: localhost + password: '' + + + + +# +----------------------------------------------------------------------------------------------+ # +# | | # +# | CUSTOMIZATION SETTINGS | # +# | | # +# | Settings that allow admins to customize the way LuckPerms operates. | # +# | | # +# +----------------------------------------------------------------------------------------------+ # + +# If LuckPerms should ensure all players have permission data when they connect to the server. +# +# - When set to true, LuckPerms will cancel login attempts if it is unable to load permissions data +# for a user, of if the storage provider is unavailable. +# - When set to false, LuckPerms will allow a player to connect regardless of whether their +# permissions data could be loaded. +# - This option does not exist on other platforms, and effectively defaults to true - however, +# the option is provided on Velocity, as it is less likely to be so dependant on permissions. +cancel-failed-logins: false + +# 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 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 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 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. +meta-formatting: + prefix: + format: + - "highest" + start-spacer: "" + middle-spacer: " " + end-spacer: "" + suffix: + format: + - "highest" + 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 + +# +----------------------------------------------------------------------------------------------+ # +# | 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 + +# +----------------------------------------------------------------------------------------------+ # +# | 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 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 + +# +----------------------------------------------------------------------------------------------+ # +# | Extra settings | # +# +----------------------------------------------------------------------------------------------+ # + +# 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. | # +# | | # +# +----------------------------------------------------------------------------------------------+ # + +# +----------------------------------------------------------------------------------------------+ # +# | 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 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 + + + + +# +----------------------------------------------------------------------------------------------+ # +# | | # +# | DEFAULT ASSIGNMENTS | # +# | | # +# +----------------------------------------------------------------------------------------------+ # + +# This section allows you to define defaults to give users whenever they connect to the server. +# The default assignments are highly configurable and conditional. +# +# There is one default assignment built into LuckPerms, which will add all users to the "default" +# group if they are not a member of any other group. This setting cannot be disabled. However, you +# can use this section to add more of your own. +# +# IMPORTANT: +# In order to save storage space, LuckPerms does not store users who have no permissions defined, +# and are only a member of the default group. Adding default assignments to this section will negate +# this effect. It is HIGHLY RECCOMENDED that instead of assigning defaults here, you add permissions +# to the "default" group, or set the "default" group to inherit other groups, and then use the +# group-name-rewrite rule above. +# +# It is also important to note that these rules are considered every time a player logs into the +# server, and are applied directly to the user's data. Simply removing a rule here will not reverse +# the effect of that rule on any users who have already had it applied to them. +# +# The "has" and "lacks" conditions below support standard boolean logic, using the 'and' & 'or' +# characters used in Java. +# e.g. "(some.other.permission | some.permission.other) & some.thing.else" == a user has +# 'some.other.permission', or 'some.permission.other', and they also have 'some.thing.else' +# +# Groups are represented by the permission node: group. +# Per server and per world nodes are represented by "server-world/permission" or "server/permission" +# +# Within conditions, permission nodes MUST be escaped using "<" and ">". See the example below. +# +# Explanation of the examples below: (they're just to demonstrate the features & use cases) +# +# rule1: +# If a user is either in the vip or vip+ group, and they have the "titles.titlecollector" permission +# set to true, and the "some.random.permission" set to false... if they're not in the group +# "prison_titlepack" on the "prison" server, then give add them to the "prison_titlepack" group on +# the "prison" server, and remove "some.random.permission". +# +# rule2: +# If the user isn't in any of the following groups on the skyblock server: sb_level1, sb_level2, +# sb_level3, then add them to sb_level1 on the skyblock server. +# +# rule3: +# If the user is a member of the default group, remove them from default, add them to member, and +# set their primary group to member. +# +# WARNING: Unlike internal commands, this system does not ensure that a group exists before adding +# a user to it. It also does not unsure that a user is a member of a group before making that group +# their primary group. +# +# Before you use "give: group." or "set-primary-group", make sure that the group exists, and +# that the user is a member of the group. +default-assignments: +# rule1: +# if: +# has-true: ( | ) & +# has-false: +# lacks: +# give: +# - prison/group.prison_titlepack +# take: +# - some.random.permission +# rule2: +# if: +# lacks: & & +# give: +# - skyblock/group.sb_level1 +# rule3: +# if: +# has-true: +# take: +# - group.default +# give: +# - group.member +# set-primary-group: member