Add support for Velocity

This commit is contained in:
Luck 2018-08-26 18:41:09 +01:00
parent bd41286076
commit 55220e9d10
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
40 changed files with 2399 additions and 154 deletions

View File

@ -37,7 +37,8 @@ public enum PlatformType {
BUKKIT("Bukkit"),
BUNGEE("Bungee"),
SPONGE("Sponge"),
NUKKIT("Nukkit");
NUKKIT("Nukkit"),
VELOCITY("Velocity");
private final String friendlyName;

View File

@ -42,9 +42,9 @@ public class BukkitMessagingFactory extends MessagingFactory<LPBukkitPlugin> {
@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<LPBukkitPlugin> {
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<LPBukkitPlugin> {
@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;
}
}
}

View File

@ -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<? extends Player> players = BungeeMessenger.this.plugin.getBootstrap().getServer().getOnlinePlayers();
Collection<? extends Player> 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);

View File

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

View File

@ -42,9 +42,9 @@ public class BungeeMessagingFactory extends MessagingFactory<LPBungeePlugin> {
@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<LPBungeePlugin> {
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<LPBungeePlugin> {
@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;
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,123 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.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<? extends ConfigurationNode> createLoader(Path path);
@Override
public void reload() {
ConfigurationLoader<? extends ConfigurationNode> 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<String> getList(String path, List<String> def) {
ConfigurationNode node = resolvePath(path);
if (node.isVirtual()) {
return def;
}
return node.getList(Object::toString);
}
@Override
public List<String> getKeys(String path, List<String> 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<String, String> getMap(String path, Map<String, String> def) {
ConfigurationNode node = resolvePath(path);
if (node.isVirtual()) {
return def;
}
Map<String, Object> m = (Map<String, Object>) node.getValue(Collections.emptyMap());
return m.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue().toString()));
}
}

View File

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

View File

@ -153,7 +153,7 @@ public class DependencyManager {
try {
// apply remap rules
List<Relocation> 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);

View File

@ -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<Relocation> getLegacyRelocations(Dependency dependency) {
if (RelocationHandler.DEPENDENCIES.contains(dependency)) {
return ImmutableList.of();
public void applyRelocationSettings(Dependency dependency, List<Relocation> 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) {

View File

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

View File

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

View File

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

View File

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

View File

@ -202,10 +202,6 @@ public class LPSpongeBootstrap implements LuckPermsBootstrap {
return this.spongeScheduler;
}
public Path getConfigPath() {
return this.configDirectory;
}
public PluginContainer getPluginContainer() {
return this.pluginContainer;
}

View File

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

View File

@ -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<CommentedConfigurationNode> 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<String> getList(String path, List<String> def) {
ConfigurationNode node = resolvePath(path);
if (node.isVirtual()) {
return def;
}
return node.getList(Object::toString);
}
@Override
public List<String> getKeys(String path, List<String> 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<String, String> getMap(String path, Map<String, String> def) {
ConfigurationNode node = resolvePath(path);
if (node.isVirtual()) {
return def;
}
Map<String, Object> m = (Map<String, Object>) node.getValue(Collections.emptyMap());
return m.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue().toString()));
protected ConfigurationLoader<? extends ConfigurationNode> createLoader(Path path) {
return HoconConfigurationLoader.builder().setPath(path).build();
}
}

View File

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

View File

@ -42,9 +42,9 @@ public class SpongeMessagingFactory extends MessagingFactory<LPSpongePlugin> {
@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<LPSpongePlugin> {
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;
}
}

View File

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

54
velocity/build.gradle Normal file
View File

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

View File

@ -0,0 +1,241 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<Player> getPlayer(UUID uuid) {
return this.proxy.getPlayer(uuid);
}
@Override
public Optional<UUID> lookupUuid(String username) {
return Optional.empty();
}
@Override
public Optional<String> lookupUsername(UUID uuid) {
return Optional.empty();
}
@Override
public int getPlayerCount() {
return this.proxy.getPlayerCount();
}
@Override
public Stream<String> getPlayerList() {
return this.proxy.getAllPlayers().stream().map(Player::getUsername);
}
@Override
public Stream<UUID> 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();
}
}

View File

@ -0,0 +1,236 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<Player> 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<Dependency> 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<Contexts> getContextForUser(User user) {
return this.bootstrap.getPlayer(user.getUuid()).map(player -> this.contextManager.getApplicableContexts(player));
}
@Override
public Stream<Sender> 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<Player> getContextManager() {
return this.contextManager;
}
}

View File

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

View File

@ -0,0 +1,68 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<String> arguments = CommandManager.stripQuotes(ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args)));
this.manager.onCommand(lpSender, "lpv", arguments);
}
@Override
public List<String> suggest(@NonNull CommandSource source, String[] args) {
Sender lpSender = this.plugin.getSenderFactory().wrap(source);
List<String> arguments = CommandManager.stripQuotes(TAB_COMPLETE_ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args)));
return this.manager.onTabComplete(lpSender, arguments);
}
}

View File

@ -0,0 +1,47 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<? extends ConfigurationNode> createLoader(Path path) {
return YAMLConfigurationLoader.builder().setPath(path).build();
}
}

View File

@ -0,0 +1,55 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<PluginContainer> {
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.");
}
}

View File

@ -0,0 +1,87 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<ScheduledTask> 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);
}
}

View File

@ -0,0 +1,81 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<CommandSource> {
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);
}
}

View File

@ -0,0 +1,64 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<PermissionProcessor> 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());
}
}

View File

@ -0,0 +1,61 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<Player> {
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;
}
}

View File

@ -0,0 +1,76 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package 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<Player> {
private final LoadingCache<Player, ContextsCache<Player>> 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<Player> cache = this.subjectCaches.getIfPresent(subject);
if (cache != null) {
cache.invalidate();
}
}
@Override
public Contexts formContexts(Player subject, ImmutableContextSet contextSet) {
return formContexts(contextSet);
}
}

View File

@ -0,0 +1,105 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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();
}
}

View File

@ -0,0 +1,169 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<UUID> 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();
}
});
}
}

View File

@ -0,0 +1,109 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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;
}
}

View File

@ -0,0 +1,72 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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<LPVelocityPlugin> {
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;
}
}
}

View File

@ -0,0 +1,63 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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() {}
}

View File

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

View File

@ -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 <on|off>'
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_<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_<track> Same as above, except takes the value with the lowest weight.
#
# => highest_not_on_track_<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_<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.<group name>
# 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.<name>" 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: (<group.vip> | <group.vip+>) & <titles.tilecollector>
# has-false: <some.random.permission>
# lacks: <prison/group.prison_titlepack>
# give:
# - prison/group.prison_titlepack
# take:
# - some.random.permission
# rule2:
# if:
# lacks: <skyblock/group.sb_level1> & <skyblock/group.sb_level2> & <skyblock/group.sb_level3>
# give:
# - skyblock/group.sb_level1
# rule3:
# if:
# has-true: <group.default>
# take:
# - group.default
# give:
# - group.member
# set-primary-group: member