Vault & LuckPerms permission integrations

This commit is contained in:
Vankka 2022-01-18 22:15:43 +02:00
parent 2ab83dfc28
commit 8fdd5e73e3
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
11 changed files with 307 additions and 27 deletions

View File

@ -24,6 +24,9 @@ dependencies {
// Adventure
runtimeDownloadApi 'net.kyori:adventure-platform-bukkit:' + rootProject.adventurePlatformVersion
// Integrations
compileOnly "com.github.MilkBowl:VaultAPI:1.7"
}
shadowJar {

View File

@ -24,6 +24,7 @@ import com.discordsrv.bukkit.config.main.BukkitConfig;
import com.discordsrv.bukkit.config.manager.BukkitConfigManager;
import com.discordsrv.bukkit.config.manager.BukkitConnectionConfigManager;
import com.discordsrv.bukkit.console.BukkitConsole;
import com.discordsrv.bukkit.integration.VaultIntegration;
import com.discordsrv.bukkit.listener.BukkitChatListener;
import com.discordsrv.bukkit.listener.BukkitDeathListener;
import com.discordsrv.bukkit.listener.BukkitStatusMessageListener;
@ -32,6 +33,7 @@ import com.discordsrv.bukkit.scheduler.BukkitScheduler;
import com.discordsrv.common.config.manager.ConnectionConfigManager;
import com.discordsrv.common.config.manager.MainConfigManager;
import com.discordsrv.common.logging.Logger;
import com.discordsrv.common.module.ModuleInitializationFunction;
import com.discordsrv.common.server.ServerDiscordSRV;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Server;
@ -137,5 +139,16 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
server().getPluginManager().registerEvents(BukkitChatListener.get(this), plugin());
server().getPluginManager().registerEvents(new BukkitDeathListener(this), plugin());
server().getPluginManager().registerEvents(new BukkitStatusMessageListener(this), plugin());
for (ModuleFunction function : new ModuleFunction[]{
VaultIntegration::new
}) {
try {
registerModule(function.initialize(this));
} catch (Throwable ignored) {}
}
}
private interface ModuleFunction extends ModuleInitializationFunction<BukkitDiscordSRV> {}
}

View File

@ -0,0 +1,188 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.bukkit.integration;
import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.common.function.CheckedSupplier;
import com.discordsrv.common.module.type.PermissionDataProvider;
import com.discordsrv.common.module.type.PluginIntegration;
import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.OfflinePlayer;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicesManager;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class VaultIntegration extends PluginIntegration<BukkitDiscordSRV> implements PermissionDataProvider {
private Permission permission;
private Chat chat;
public VaultIntegration(BukkitDiscordSRV discordSRV) {
super(discordSRV);
}
@Override
public int priority() {
// Lower priority than default
return -1;
}
@Override
public boolean isEnabled() {
try {
Class.forName("net.milkbowl.vault.permission.Permission");
} catch (ClassNotFoundException e) {
return false;
}
return super.isEnabled();
}
@Override
public void enable() {
ServicesManager servicesManager = discordSRV.plugin().getServer().getServicesManager();
RegisteredServiceProvider<Permission> permissionRSP = servicesManager.getRegistration(Permission.class);
if (permissionRSP != null) {
permission = permissionRSP.getProvider();
}
RegisteredServiceProvider<Chat> chatRSP = servicesManager.getRegistration(Chat.class);
if (chatRSP != null) {
chat = chatRSP.getProvider();
}
}
@Override
public void disable() {
permission = null;
chat = null;
}
@Override
public boolean supportsOffline() {
// Maybe
return true;
}
private <T> CompletableFuture<T> unsupported() {
CompletableFuture<T> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException("Vault backend unavailable"));
return future;
}
private <T> CompletableFuture<T> supply(CheckedSupplier<T> supplier) {
CompletableFuture<T> future = new CompletableFuture<>();
discordSRV.scheduler().runFork(() -> {
try {
future.complete(supplier.get());
} catch (Throwable e) {
future.completeExceptionally(e);
}
});
return future;
}
private OfflinePlayer offlinePlayer(UUID player) {
return discordSRV.plugin().getServer().getOfflinePlayer(player);
}
@Override
public CompletableFuture<Boolean> hasGroup(UUID player, String groupName) {
if (permission == null) {
return unsupported();
}
return supply(() -> {
OfflinePlayer offlinePlayer = offlinePlayer(player);
return permission.playerInGroup(null, offlinePlayer, groupName);
});
}
@Override
public CompletableFuture<Void> addGroup(UUID player, String groupName) {
if (permission == null) {
return unsupported();
}
return supply(() -> {
OfflinePlayer offlinePlayer = offlinePlayer(player);
permission.playerAddGroup(null, offlinePlayer, groupName);
return null;
});
}
@Override
public CompletableFuture<Void> removeGroup(UUID player, String groupName) {
if (permission == null) {
return unsupported();
}
return supply(() -> {
OfflinePlayer offlinePlayer = offlinePlayer(player);
permission.playerRemoveGroup(null, offlinePlayer, groupName);
return null;
});
}
@Override
public CompletableFuture<Boolean> hasPermission(UUID player, String permissionNode) {
if (permission == null) {
return unsupported();
}
return supply(() -> {
OfflinePlayer offlinePlayer = offlinePlayer(player);
return permission.playerHas(null, offlinePlayer, permissionNode);
});
}
@Override
public CompletableFuture<String> getPrefix(UUID player) {
if (chat == null) {
return unsupported();
}
return supply(() -> {
OfflinePlayer offlinePlayer = offlinePlayer(player);
return chat.getPlayerPrefix(null, offlinePlayer);
});
}
@Override
public CompletableFuture<String> getSuffix(UUID player) {
if (chat == null) {
return unsupported();
}
return supply(() -> {
OfflinePlayer offlinePlayer = offlinePlayer(player);
return chat.getPlayerSuffix(null, offlinePlayer);
});
}
@Override
public CompletableFuture<String> getMeta(UUID player, String key) throws UnsupportedOperationException {
// :(
throw new UnsupportedOperationException("Vault does not support this operation");
}
}

View File

@ -42,7 +42,7 @@ public abstract class ServerDiscordSRV<C extends MainConfig, CC extends Connecti
protected void enable() throws Throwable {
super.enable();
for (ModuleInitializationFunction function : new ModuleInitializationFunction[]{
for (ModuleFunction function : new ModuleFunction[]{
DeathMessageModule::new
}) {
try {
@ -51,6 +51,8 @@ public abstract class ServerDiscordSRV<C extends MainConfig, CC extends Connecti
}
}
private interface ModuleFunction extends ModuleInitializationFunction<ServerDiscordSRV<?, ?>> {}
public final CompletableFuture<Void> invokeServerStarted() {
return invokeLifecycle(this::serverStarted, "Failed to enable", true);
}

View File

@ -183,12 +183,12 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
}
@Override
public void registerModule(AbstractModule module) {
public void registerModule(AbstractModule<?> module) {
moduleManager.register(module);
}
@Override
public void unregisterModule(AbstractModule module) {
public void unregisterModule(AbstractModule<?> module) {
moduleManager.unregister(module);
}
@ -302,7 +302,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
// Register modules
moduleManager = new ModuleManager(this);
for (ModuleInitializationFunction function : new ModuleInitializationFunction[]{
for (ModuleFunction function : new ModuleFunction[]{
ChannelUpdaterModule::new,
GlobalChannelLookupModule::new,
@ -323,6 +323,8 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
}
}
private interface ModuleFunction extends ModuleInitializationFunction<DiscordSRV> {}
@OverridingMethodsMustInvokeSuper
protected void disable() {
Status status = this.status.get();

View File

@ -85,8 +85,8 @@ public interface DiscordSRV extends DiscordSRVApi {
// Modules
<T extends Module> T getModule(Class<T> moduleType);
void registerModule(AbstractModule module);
void unregisterModule(AbstractModule module);
void registerModule(AbstractModule<?> module);
void unregisterModule(AbstractModule<?> module);
Locale locale();

View File

@ -23,14 +23,19 @@ import com.discordsrv.common.module.type.PermissionDataProvider;
import com.discordsrv.common.module.type.PluginIntegration;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.model.data.DataMutateResult;
import net.luckperms.api.model.data.NodeMap;
import net.luckperms.api.model.group.Group;
import net.luckperms.api.model.user.User;
import net.luckperms.api.node.Node;
import net.luckperms.api.node.types.InheritanceNode;
import net.luckperms.api.query.QueryOptions;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
public class LuckPermsIntegration extends PluginIntegration implements PermissionDataProvider {
public class LuckPermsIntegration extends PluginIntegration<DiscordSRV> implements PermissionDataProvider {
private LuckPerms luckPerms;
@ -46,7 +51,7 @@ public class LuckPermsIntegration extends PluginIntegration implements Permissio
return false;
}
return true;
return super.isEnabled();
}
@Override
@ -59,24 +64,72 @@ public class LuckPermsIntegration extends PluginIntegration implements Permissio
luckPerms = null;
}
private CompletableFuture<User> user(UUID player) {
return luckPerms.getUserManager().loadUser(player);
}
@Override
public boolean hasGroup(UUID player, String groupName) {
User user = luckPerms.getUserManager().getUser(player);
if (user == null) {
public boolean supportsOffline() {
return true;
}
@Override
public CompletableFuture<Boolean> hasGroup(UUID player, String groupName) {
return user(player).thenApply(user -> {
for (Group inheritedGroup : user.getInheritedGroups(QueryOptions.defaultContextualOptions())) {
if (inheritedGroup.getName().equalsIgnoreCase(groupName)) {
return true;
}
}
return false;
});
}
@Override
public CompletableFuture<Void> addGroup(UUID player, String groupName) {
return groupMutate(player, groupName, NodeMap::add);
}
@Override
public CompletableFuture<Void> removeGroup(UUID player, String groupName) {
return groupMutate(player, groupName, NodeMap::remove);
}
private CompletableFuture<Void> groupMutate(UUID player, String groupName, BiFunction<NodeMap, Node, DataMutateResult> function) {
Group group = luckPerms.getGroupManager().getGroup(groupName);
if (group == null) {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException("Group does not exist"));
return future;
}
Collection<Group> groups = user.getInheritedGroups(QueryOptions.defaultContextualOptions());
return groups.stream().anyMatch(group -> group.getName().equalsIgnoreCase(groupName));
return user(player).thenApply(user -> {
DataMutateResult result = function.apply(user.data(), InheritanceNode.builder(group).build());
if (result != DataMutateResult.SUCCESS) {
throw new RuntimeException(result.name());
}
return null;
});
}
@Override
public void addGroup(UUID player, String groupName) {
public CompletableFuture<Boolean> hasPermission(UUID player, String permission) {
return user(player).thenApply(
user -> user.getCachedData().getPermissionData().checkPermission(permission).asBoolean());
}
@Override
public void removeGroup(UUID player, String groupName) {
public CompletableFuture<String> getPrefix(UUID player) {
return user(player).thenApply(user -> user.getCachedData().getMetaData().getPrefix());
}
@Override
public CompletableFuture<String> getSuffix(UUID player) {
return user(player).thenApply(user -> user.getCachedData().getMetaData().getSuffix());
}
@Override
public CompletableFuture<String> getMeta(UUID player, String key) throws UnsupportedOperationException {
return user(player).thenApply(user -> user.getCachedData().getMetaData().getMetaValue(key));
}
}

View File

@ -22,8 +22,8 @@ import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.module.type.AbstractModule;
@FunctionalInterface
public interface ModuleInitializationFunction {
public interface ModuleInitializationFunction<DT extends DiscordSRV> {
AbstractModule initialize(DiscordSRV discordSRV) throws Throwable;
AbstractModule<DT> initialize(DT discordSRV) throws Throwable;
}

View File

@ -23,12 +23,12 @@ import com.discordsrv.api.event.events.Processable;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.event.util.EventUtil;
public abstract class AbstractModule implements Module {
public abstract class AbstractModule<DT extends DiscordSRV> implements Module {
protected final DiscordSRV discordSRV;
protected final DT discordSRV;
private boolean hasBeenEnabled = false;
public AbstractModule(DiscordSRV discordSRV) {
public AbstractModule(DT discordSRV) {
this.discordSRV = discordSRV;
}

View File

@ -19,10 +19,21 @@
package com.discordsrv.common.module.type;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public interface PermissionDataProvider extends Module {
boolean hasGroup(UUID player, String groupName);
void addGroup(UUID player, String groupName);
void removeGroup(UUID player, String groupName);
boolean supportsOffline();
CompletableFuture<Boolean> hasGroup(UUID player, String groupName);
CompletableFuture<Void> addGroup(UUID player, String groupName);
CompletableFuture<Void> removeGroup(UUID player, String groupName);
CompletableFuture<Boolean> hasPermission(UUID player, String permission);
CompletableFuture<String> getPrefix(UUID player);
CompletableFuture<String> getSuffix(UUID player);
CompletableFuture<String> getMeta(UUID player, String key) throws UnsupportedOperationException;
}

View File

@ -20,9 +20,17 @@ package com.discordsrv.common.module.type;
import com.discordsrv.common.DiscordSRV;
public abstract class PluginIntegration extends AbstractModule {
import javax.annotation.OverridingMethodsMustInvokeSuper;
public PluginIntegration(DiscordSRV discordSRV) {
public abstract class PluginIntegration<DT extends DiscordSRV> extends AbstractModule<DT> {
public PluginIntegration(DT discordSRV) {
super(discordSRV);
}
@Override
@OverridingMethodsMustInvokeSuper
public boolean isEnabled() {
return super.isEnabled();
}
}