mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-10-31 08:32:18 +01:00
In-game commands
This commit is contained in:
parent
90150325bf
commit
99a87382bd
@ -35,7 +35,7 @@ import net.dv8tion.jda.api.JDA;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The DiscordSRV API.
|
||||
@ -215,4 +215,27 @@ public interface DiscordSRVApi {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum ReloadFlag {
|
||||
CONFIG(false),
|
||||
LINKED_ACCOUNT_PROVIDER(false),
|
||||
STORAGE(true),
|
||||
DISCORD_CONNECTION(true),
|
||||
MODULES(false),
|
||||
|
||||
;
|
||||
|
||||
public static final Set<ReloadFlag> ALL = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(values())));
|
||||
public static final Set<ReloadFlag> DEFAULT_FLAGS = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(CONFIG, MODULES)));
|
||||
|
||||
private final boolean requiresConfirm;
|
||||
|
||||
ReloadFlag(boolean requiresConfirm) {
|
||||
this.requiresConfirm = requiresConfirm;
|
||||
}
|
||||
|
||||
public boolean requiresConfirm() {
|
||||
return requiresConfirm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,15 +25,9 @@ package com.discordsrv.api.event.events.lifecycle;
|
||||
|
||||
import com.discordsrv.api.event.events.Event;
|
||||
|
||||
public class DiscordSRVReloadEvent implements Event {
|
||||
/**
|
||||
* Indicates that DiscordSRV's Discord connection has been (re-)established. This may run more than once.
|
||||
*/
|
||||
public class DiscordSRVConnectedEvent implements Event {
|
||||
|
||||
private final boolean config;
|
||||
|
||||
public DiscordSRVReloadEvent(boolean config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public boolean isConfig() {
|
||||
return config;
|
||||
}
|
||||
}
|
@ -26,8 +26,7 @@ package com.discordsrv.api.event.events.lifecycle;
|
||||
import com.discordsrv.api.event.events.Event;
|
||||
|
||||
/**
|
||||
* Indicates that DiscordSRV's systems (including Discord connection) are ready.
|
||||
* Indicates that DiscordSRV is ready (including Discord connection), this only runs once when DiscordSRV enables.
|
||||
*/
|
||||
public class DiscordSRVReadyEvent implements Event {
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of the DiscordSRV API, licensed under the MIT License
|
||||
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV 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 com.discordsrv.api.event.events.lifecycle;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.event.events.Event;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An event for when DiscordSRV successfully reloads partially or completely.
|
||||
*/
|
||||
public class DiscordSRVReloadedEvent implements Event {
|
||||
|
||||
private final Set<DiscordSRVApi.ReloadFlag> flags;
|
||||
|
||||
public DiscordSRVReloadedEvent(Set<DiscordSRVApi.ReloadFlag> flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of DiscordSRV systems that were reloaded.
|
||||
* @return an unmodifiable set of {@link com.discordsrv.api.DiscordSRVApi.ReloadFlag}s
|
||||
*/
|
||||
public Set<DiscordSRVApi.ReloadFlag> flags() {
|
||||
return flags;
|
||||
}
|
||||
}
|
12
build.gradle
12
build.gradle
@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id 'com.github.johnrengelman.shadow' version '7.1.1' apply false
|
||||
id 'org.cadixdev.licenser' version '0.6.0' apply false
|
||||
id 'org.cadixdev.licenser' version '0.6.1' apply false
|
||||
id 'net.kyori.blossom' version '1.2.0' apply false
|
||||
id 'dev.vankka.dependencydownload.plugin' version '1.1.5-SNAPSHOT' apply false
|
||||
}
|
||||
@ -17,8 +17,8 @@ ext {
|
||||
// Configurate
|
||||
configurateVersion = '4.1.2'
|
||||
// Adventure & Adventure Platform
|
||||
adventureVersion = '4.9.1'
|
||||
adventurePlatformVersion = '4.0.0'
|
||||
adventureVersion = '4.10.0'
|
||||
adventurePlatformVersion = '4.1.0'
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@ -59,6 +59,12 @@ allprojects {
|
||||
includeGroup 'me.scarsz'
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url 'https://libraries.minecraft.net'
|
||||
content {
|
||||
includeGroup 'com.mojang'
|
||||
}
|
||||
}
|
||||
|
||||
// Get dependencies from central last, everything else should be filtered
|
||||
mavenCentral()
|
||||
|
@ -1,5 +1,30 @@
|
||||
import dev.vankka.dependencydownload.task.GenerateDependencyDownloadResourceTask
|
||||
|
||||
apply from: rootProject.file('buildscript/runtime.gradle')
|
||||
|
||||
configurations {
|
||||
commodore
|
||||
compileOnly.extendsFrom commodore
|
||||
}
|
||||
|
||||
task generateResourceForCommodore(type: GenerateDependencyDownloadResourceTask) {
|
||||
var conf = configurations.commodore
|
||||
configuration = conf
|
||||
file = 'dependencies/' + conf.name + '.txt'
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = 'bukkit.jarinjar'
|
||||
|
||||
[
|
||||
'net.kyori',
|
||||
'me.lucko.commodore'
|
||||
].each {
|
||||
relocate it, 'com.discordsrv.dependencies.' + it
|
||||
}
|
||||
// More relocations in buildscript/relocations.gradle
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
exclusiveContent {
|
||||
@ -41,13 +66,18 @@ dependencies {
|
||||
// Adventure
|
||||
runtimeDownloadApi 'net.kyori:adventure-platform-bukkit:' + rootProject.adventurePlatformVersion
|
||||
|
||||
// Commodore
|
||||
commodore('me.lucko:commodore:1.10') {
|
||||
// We only use commodore when it's included in the server, so we don't want to download it
|
||||
exclude module: 'brigadier'
|
||||
// We don't use the commodore file format
|
||||
exclude module: 'commodore-file'
|
||||
}
|
||||
|
||||
// Integrations
|
||||
compileOnly 'net.milkbowl.vault:VaultAPI:1.7'
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = 'bukkit.jarinjar'
|
||||
|
||||
relocate 'net.kyori', 'com.discordsrv.dependencies.net.kyori'
|
||||
// More relocations in buildscript/relocations.gradle
|
||||
processResources {
|
||||
dependsOn(generateResourceForCommodore)
|
||||
}
|
||||
|
@ -5,3 +5,14 @@ description: ""
|
||||
authors: [Scarsz, Vankka]
|
||||
load: STARTUP
|
||||
api-version: 1.13
|
||||
|
||||
softdepend: [
|
||||
# Permission + group providers
|
||||
Vault, LuckPerms,
|
||||
# Adventure
|
||||
ViaVersion
|
||||
]
|
||||
|
||||
commands:
|
||||
discordsrv:
|
||||
description: "DiscordSRV's primary command"
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.discordsrv.bukkit;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.bukkit.command.game.handler.AbstractBukkitCommandHandler;
|
||||
import com.discordsrv.bukkit.config.connection.BukkitConnectionConfig;
|
||||
import com.discordsrv.bukkit.config.main.BukkitConfig;
|
||||
import com.discordsrv.bukkit.config.manager.BukkitConfigManager;
|
||||
@ -32,6 +33,7 @@ import com.discordsrv.bukkit.listener.BukkitStatusMessageListener;
|
||||
import com.discordsrv.bukkit.player.BukkitPlayerProvider;
|
||||
import com.discordsrv.bukkit.plugin.BukkitPluginManager;
|
||||
import com.discordsrv.bukkit.scheduler.BukkitScheduler;
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
||||
import com.discordsrv.common.config.manager.MainConfigManager;
|
||||
import com.discordsrv.common.debug.data.OnlineMode;
|
||||
@ -60,6 +62,7 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
|
||||
private final BukkitConsole console;
|
||||
private final BukkitPlayerProvider playerProvider;
|
||||
private final BukkitPluginManager pluginManager;
|
||||
private AbstractBukkitCommandHandler commandHandler;
|
||||
|
||||
private final BukkitConnectionConfigManager connectionConfigManager;
|
||||
private final BukkitConfigManager configManager;
|
||||
@ -157,6 +160,11 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
|
||||
return bootstrap.getClasspathAppender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICommandHandler commandHandler() {
|
||||
return commandHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionConfigManager<BukkitConnectionConfig> connectionConfigManager() {
|
||||
return connectionConfigManager;
|
||||
@ -176,15 +184,19 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
|
||||
this.audiences = BukkitAudiences.create(bootstrap.getPlugin());
|
||||
server().getPluginManager().registerEvents(new BukkitConnectionListener(this), plugin());
|
||||
|
||||
super.enable();
|
||||
// Command handler
|
||||
commandHandler = AbstractBukkitCommandHandler.get(this);
|
||||
|
||||
// Register listeners
|
||||
server().getPluginManager().registerEvents(BukkitChatListener.get(this), plugin());
|
||||
server().getPluginManager().registerEvents(new BukkitDeathListener(this), plugin());
|
||||
server().getPluginManager().registerEvents(new BukkitStatusMessageListener(this), plugin());
|
||||
|
||||
// Modules
|
||||
registerModule(VaultIntegration::new);
|
||||
registerModule(MinecraftToDiscordChatModule::new);
|
||||
|
||||
super.enable();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ import dev.vankka.mcdependencydownload.classloader.JarInJarClassLoader;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DiscordSRVBukkitBootstrap extends BukkitBootstrap {
|
||||
|
||||
@ -40,11 +42,25 @@ public class DiscordSRVBukkitBootstrap extends BukkitBootstrap {
|
||||
this.dependencies = new InitialDependencyLoader(
|
||||
logger,
|
||||
plugin.getDataFolder().toPath(),
|
||||
new String[] {"dependencies/runtimeDownload-bukkit.txt"},
|
||||
getDependencyResources(),
|
||||
getClasspathAppender()
|
||||
);
|
||||
}
|
||||
|
||||
private static String[] getDependencyResources() {
|
||||
List<String> resources = new ArrayList<>();
|
||||
resources.add("dependencies/runtimeDownload-bukkit.txt");
|
||||
|
||||
try {
|
||||
Class.forName("com.mojang.brigadier.CommandDispatcher");
|
||||
resources.add("dependencies/commodore.txt");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// CommandDispatches not present, don't need to bother downloading commodore
|
||||
}
|
||||
|
||||
return resources.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
dependencies.loadAndEnable(() -> this.discordSRV = new BukkitDiscordSRV(this, logger));
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.command.game.handler;
|
||||
|
||||
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||
import com.discordsrv.common.command.game.handler.BasicCommandHandler;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public abstract class AbstractBukkitCommandExecutor extends AbstractBukkitCommandHandler implements CommandExecutor {
|
||||
|
||||
protected final BasicCommandHandler handler = new BasicCommandHandler();
|
||||
|
||||
public AbstractBukkitCommandExecutor(BukkitDiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull Command command,
|
||||
@NotNull String label,
|
||||
@NotNull String[] args
|
||||
) {
|
||||
handler.execute(sender(sender), label, Arrays.asList(args));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.command.game.handler;
|
||||
|
||||
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||
import com.discordsrv.bukkit.command.game.sender.BukkitCommandSender;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Locale;
|
||||
|
||||
public abstract class AbstractBukkitCommandHandler implements ICommandHandler {
|
||||
|
||||
public static AbstractBukkitCommandHandler get(BukkitDiscordSRV discordSRV) {
|
||||
try {
|
||||
Class.forName("me.lucko.commodore.Commodore");
|
||||
return new CommodoreHandler(discordSRV);
|
||||
} catch (Throwable e) {
|
||||
BukkitBasicCommandHandler handler = new BukkitBasicCommandHandler(discordSRV);
|
||||
if (e instanceof ClassNotFoundException) {
|
||||
handler.logger.debug("Brigadier classes not present, not using commodore");
|
||||
} else {
|
||||
handler.logger.debug("Failed to initialize Commodore", e);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
protected final BukkitDiscordSRV discordSRV;
|
||||
protected final Logger logger;
|
||||
|
||||
public AbstractBukkitCommandHandler(BukkitDiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.logger = new NamedLogger(discordSRV, "COMMAND_HANDLER");
|
||||
}
|
||||
|
||||
protected ICommandSender sender(CommandSender commandSender) {
|
||||
if (commandSender instanceof Player) {
|
||||
return discordSRV.playerProvider().player((Player) commandSender);
|
||||
} else if (commandSender instanceof ConsoleCommandSender) {
|
||||
return discordSRV.console();
|
||||
} else {
|
||||
return new BukkitCommandSender(discordSRV, commandSender, () -> discordSRV.audiences().sender(commandSender));
|
||||
}
|
||||
}
|
||||
|
||||
protected PluginCommand command(GameCommand gameCommand) {
|
||||
String label = gameCommand.getLabel();
|
||||
PluginCommand pluginCommand = discordSRV.plugin().getCommand(label);
|
||||
if (pluginCommand != null) {
|
||||
return pluginCommand;
|
||||
}
|
||||
|
||||
PluginCommand command = null;
|
||||
try {
|
||||
Constructor<?> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
||||
if (!constructor.isAccessible()) {
|
||||
constructor.setAccessible(true);
|
||||
}
|
||||
command = (PluginCommand) constructor.newInstance(label, discordSRV.plugin());
|
||||
discordSRV.server().getCommandMap().register(label, discordSRV.plugin().getName().toLowerCase(Locale.ROOT), command);
|
||||
} catch (ReflectiveOperationException ignored) {}
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.command.game.handler;
|
||||
|
||||
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class BukkitBasicCommandHandler extends AbstractBukkitCommandExecutor implements TabCompleter {
|
||||
|
||||
public BukkitBasicCommandHandler(BukkitDiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull Command command,
|
||||
@NotNull String alias,
|
||||
@NotNull String[] args
|
||||
) {
|
||||
return handler.suggest(sender(sender), alias, Arrays.asList(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCommand(GameCommand command) {
|
||||
PluginCommand pluginCommand = command(command);
|
||||
if (pluginCommand == null) {
|
||||
logger.error("Failed to create command " + command.getLabel());
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Registering command " + command.getLabel() + " with basic handler");
|
||||
|
||||
handler.registerCommand(command);
|
||||
pluginCommand.setExecutor(this);
|
||||
pluginCommand.setTabCompleter(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.command.game.handler;
|
||||
|
||||
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.handler.util.BrigadierUtil;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import me.lucko.commodore.Commodore;
|
||||
import me.lucko.commodore.CommodoreProvider;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
|
||||
/**
|
||||
* No avoiding basic handler on bukkit. Unfortunately it isn't possible to use brigadier for executing.
|
||||
*/
|
||||
public class CommodoreHandler extends AbstractBukkitCommandExecutor {
|
||||
|
||||
private final Commodore commodore;
|
||||
|
||||
public CommodoreHandler(BukkitDiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
this.commodore = CommodoreProvider.getCommodore(discordSRV.plugin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCommand(GameCommand command) {
|
||||
logger.debug("Registering command " + command.getLabel() + " with commodore");
|
||||
|
||||
LiteralCommandNode<?> argumentBuilder = BrigadierUtil.convertToBrigadier(
|
||||
command,
|
||||
wrapper -> sender(commodore.getBukkitSender(wrapper))
|
||||
);
|
||||
CommandNode<?> redirection = argumentBuilder.getRedirect();
|
||||
if (redirection != null) {
|
||||
// Commodore handles the label being different fine
|
||||
argumentBuilder = (LiteralCommandNode<?>) redirection;
|
||||
}
|
||||
|
||||
PluginCommand pluginCommand = command(command);
|
||||
if (pluginCommand == null) {
|
||||
logger.error("Failed to create command " + command.getLabel());
|
||||
return;
|
||||
}
|
||||
|
||||
handler.registerCommand(command);
|
||||
pluginCommand.setExecutor(this);
|
||||
|
||||
String requiredPermission = command.getRequiredPermission();
|
||||
LiteralCommandNode<?> finalArgumentBuilder = argumentBuilder;
|
||||
discordSRV.scheduler().runOnMainThread(() -> commodore.register(
|
||||
pluginCommand,
|
||||
finalArgumentBuilder,
|
||||
player -> requiredPermission == null || player.hasPermission(requiredPermission)
|
||||
));
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.command.game.sender;
|
||||
|
||||
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class BukkitCommandSender implements ICommandSender {
|
||||
|
||||
protected final BukkitDiscordSRV discordSRV;
|
||||
protected final CommandSender commandSender;
|
||||
protected final Supplier<Audience> audienceSupplier;
|
||||
|
||||
public BukkitCommandSender(BukkitDiscordSRV discordSRV, CommandSender commandSender, Supplier<Audience> audienceSupplier) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.commandSender = commandSender;
|
||||
this.audienceSupplier = audienceSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return commandSender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
discordSRV.scheduler().runOnMainThread(() -> discordSRV.server().dispatchCommand(commandSender, command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return audienceSupplier.get();
|
||||
}
|
||||
}
|
@ -19,21 +19,19 @@
|
||||
package com.discordsrv.bukkit.console;
|
||||
|
||||
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||
import com.discordsrv.bukkit.command.game.sender.BukkitCommandSender;
|
||||
import com.discordsrv.common.console.Console;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
import com.discordsrv.common.logging.backend.LoggingBackend;
|
||||
import com.discordsrv.common.logging.backend.impl.JavaLoggerImpl;
|
||||
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BukkitConsole implements Console {
|
||||
public class BukkitConsole extends BukkitCommandSender implements Console {
|
||||
|
||||
private final BukkitDiscordSRV discordSRV;
|
||||
private final LoggingBackend loggingBackend;
|
||||
|
||||
public BukkitConsole(BukkitDiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
super(discordSRV, discordSRV.server().getConsoleSender(), () -> discordSRV.audiences().console());
|
||||
|
||||
LoggingBackend logging;
|
||||
try {
|
||||
@ -49,25 +47,8 @@ public class BukkitConsole implements Console {
|
||||
this.loggingBackend = logging;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return discordSRV.server().getConsoleSender().hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
discordSRV.scheduler().runOnMainThread(() ->
|
||||
discordSRV.server().dispatchCommand(
|
||||
discordSRV.server().getConsoleSender(), command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggingBackend loggingBackend() {
|
||||
return loggingBackend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return discordSRV.audiences().console();
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ public class BukkitOfflinePlayer implements IOfflinePlayer {
|
||||
return discordSRV;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullabilityProblems")
|
||||
@Override
|
||||
public String username() {
|
||||
return offlinePlayer.getName();
|
||||
|
@ -19,36 +19,25 @@
|
||||
package com.discordsrv.bukkit.player;
|
||||
|
||||
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||
import com.discordsrv.bukkit.command.game.sender.BukkitCommandSender;
|
||||
import com.discordsrv.bukkit.component.util.PaperComponentUtil;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import com.discordsrv.common.player.IPlayer;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings("NullableProblems") // BukkitOfflinePlayer nullability
|
||||
public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer {
|
||||
public class BukkitPlayer extends BukkitCommandSender implements IPlayer {
|
||||
|
||||
private final Player player;
|
||||
private final Audience audience;
|
||||
private final Identity identity;
|
||||
|
||||
public BukkitPlayer(BukkitDiscordSRV discordSRV, Player player) {
|
||||
super(discordSRV, player);
|
||||
super(discordSRV, player, () -> discordSRV.audiences().player(player));
|
||||
this.player = player;
|
||||
this.audience = discordSRV.audiences().player(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
discordSRV.scheduler().runOnMainThread(() ->
|
||||
discordSRV.server().dispatchCommand(player, command));
|
||||
this.identity = Identity.identity(player.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -56,6 +45,11 @@ public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer {
|
||||
return discordSRV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String username() {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Paper
|
||||
@Override
|
||||
public @NotNull Component displayName() {
|
||||
@ -65,7 +59,7 @@ public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return audience;
|
||||
public @NotNull Identity identity() {
|
||||
return identity;
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.Optional;
|
||||
@ -52,8 +52,8 @@ public class BukkitPlayerProvider extends ServerPlayerProvider<BukkitPlayer, Buk
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerLogin(PlayerLoginEvent event) {
|
||||
addPlayer(event.getPlayer(), false);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,16 @@
|
||||
apply from: rootProject.file('buildscript/runtime.gradle')
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = 'bungee.jarinjar'
|
||||
|
||||
[
|
||||
'net.kyori'
|
||||
].each {
|
||||
relocate it, 'com.discordsrv.dependencies.' + it
|
||||
}
|
||||
// More relocations in buildscript/relocations.gradle
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
exclusiveContent {
|
||||
@ -32,10 +43,3 @@ dependencies {
|
||||
runtimeDownloadApi 'net.kyori:adventure-platform-bungeecord:' + rootProject.adventurePlatformVersion
|
||||
runtimeDownloadApi 'net.kyori:adventure-text-serializer-bungeecord:' + rootProject.adventurePlatformVersion
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = 'bungee.jarinjar'
|
||||
|
||||
relocate 'net.kyori', 'com.discordsrv.dependencies.net.kyori'
|
||||
// More relocations in buildscript/relocations.gradle
|
||||
}
|
||||
|
@ -18,9 +18,11 @@
|
||||
|
||||
package com.discordsrv.bungee;
|
||||
|
||||
import com.discordsrv.bungee.command.game.handler.BungeeCommandHandler;
|
||||
import com.discordsrv.bungee.console.BungeeConsole;
|
||||
import com.discordsrv.bungee.player.BungeePlayerProvider;
|
||||
import com.discordsrv.bungee.plugin.BungeePluginManager;
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.MainConfig;
|
||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
||||
@ -49,6 +51,7 @@ public class BungeeDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConf
|
||||
private final BungeeConsole console;
|
||||
private final BungeePlayerProvider playerProvider;
|
||||
private final BungeePluginManager pluginManager;
|
||||
private BungeeCommandHandler commandHandler;
|
||||
|
||||
public BungeeDiscordSRV(DiscordSRVBungeeBootstrap bootstrap, Logger logger) {
|
||||
this.bootstrap = bootstrap;
|
||||
@ -120,6 +123,11 @@ public class BungeeDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConf
|
||||
return bootstrap.getClasspathAppender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICommandHandler commandHandler() {
|
||||
return commandHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
|
||||
return null;
|
||||
@ -135,6 +143,8 @@ public class BungeeDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConf
|
||||
// Player related
|
||||
this.audiences = BungeeAudiences.create(bootstrap.getPlugin());
|
||||
|
||||
this.commandHandler = new BungeeCommandHandler(this);
|
||||
|
||||
super.enable();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.bungee.command.game.handler;
|
||||
|
||||
import com.discordsrv.bungee.BungeeDiscordSRV;
|
||||
import com.discordsrv.bungee.command.game.sender.BungeeCommandSender;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.handler.BasicCommandHandler;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import net.md_5.bungee.api.plugin.TabExecutor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* BungeeCord has not concept of Brigadier.
|
||||
*/
|
||||
public class BungeeCommandHandler extends BasicCommandHandler {
|
||||
|
||||
private final BungeeDiscordSRV discordSRV;
|
||||
|
||||
public BungeeCommandHandler(BungeeDiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCommand(GameCommand command) {
|
||||
super.registerCommand(command);
|
||||
|
||||
discordSRV.proxy().getPluginManager().registerCommand(discordSRV.plugin(), new BungeeCommand(command));
|
||||
}
|
||||
|
||||
public ICommandSender getSender(CommandSender sender) {
|
||||
if (sender instanceof ProxiedPlayer) {
|
||||
return discordSRV.playerProvider().player((ProxiedPlayer) sender);
|
||||
} else if (sender == discordSRV.proxy().getConsole()) {
|
||||
return discordSRV.console();
|
||||
} else {
|
||||
return new BungeeCommandSender(discordSRV, sender, () -> discordSRV.audiences().sender(sender));
|
||||
}
|
||||
}
|
||||
|
||||
public class BungeeCommand extends Command implements TabExecutor {
|
||||
|
||||
private final GameCommand command;
|
||||
|
||||
public BungeeCommand(GameCommand command) {
|
||||
super(command.getLabel(), command.getRequiredPermission());
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
BungeeCommandHandler.this.execute(getSender(sender), command.getLabel(), Arrays.asList(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
return BungeeCommandHandler.this.suggest(getSender(sender), command.getLabel(), Arrays.asList(args));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.bungee.command.game.sender;
|
||||
|
||||
import com.discordsrv.bungee.BungeeDiscordSRV;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class BungeeCommandSender implements ICommandSender {
|
||||
|
||||
protected final BungeeDiscordSRV discordSRV;
|
||||
protected final CommandSender commandSender;
|
||||
protected final Supplier<Audience> audience;
|
||||
|
||||
public BungeeCommandSender(BungeeDiscordSRV discordSRV, CommandSender commandSender, Supplier<Audience> audience) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.commandSender = commandSender;
|
||||
this.audience = audience;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return commandSender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
discordSRV.proxy().getPluginManager().dispatchCommand(commandSender, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return audience.get();
|
||||
}
|
||||
}
|
@ -19,40 +19,22 @@
|
||||
package com.discordsrv.bungee.console;
|
||||
|
||||
import com.discordsrv.bungee.BungeeDiscordSRV;
|
||||
import com.discordsrv.bungee.command.game.sender.BungeeCommandSender;
|
||||
import com.discordsrv.common.console.Console;
|
||||
import com.discordsrv.common.logging.backend.LoggingBackend;
|
||||
import com.discordsrv.common.logging.backend.impl.JavaLoggerImpl;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BungeeConsole implements Console {
|
||||
public class BungeeConsole extends BungeeCommandSender implements Console {
|
||||
|
||||
private final BungeeDiscordSRV discordSRV;
|
||||
private final LoggingBackend loggingBackend;
|
||||
|
||||
public BungeeConsole(BungeeDiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
super(discordSRV, discordSRV.proxy().getConsole(), () -> discordSRV.audiences().console());
|
||||
this.loggingBackend = JavaLoggerImpl.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return discordSRV.proxy().getConsole().hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
discordSRV.proxy().getPluginManager().dispatchCommand(
|
||||
discordSRV.proxy().getConsole(), command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggingBackend loggingBackend() {
|
||||
return loggingBackend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return discordSRV.audiences().console();
|
||||
}
|
||||
}
|
||||
|
@ -19,37 +19,24 @@
|
||||
package com.discordsrv.bungee.player;
|
||||
|
||||
import com.discordsrv.bungee.BungeeDiscordSRV;
|
||||
import com.discordsrv.bungee.command.game.sender.BungeeCommandSender;
|
||||
import com.discordsrv.bungee.component.util.BungeeComponentUtil;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.player.IPlayer;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BungeePlayer implements IPlayer {
|
||||
public class BungeePlayer extends BungeeCommandSender implements IPlayer {
|
||||
|
||||
private final BungeeDiscordSRV discordSRV;
|
||||
private final ProxiedPlayer player;
|
||||
private final Identity identity;
|
||||
private final Audience audience;
|
||||
|
||||
public BungeePlayer(BungeeDiscordSRV discordSRV, ProxiedPlayer player) {
|
||||
this.discordSRV = discordSRV;
|
||||
super(discordSRV, player, () -> discordSRV.audiences().player(player));
|
||||
this.player = player;
|
||||
this.identity = Identity.identity(player.getUniqueId());
|
||||
this.audience = discordSRV.audiences().player(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
discordSRV.proxy().getPluginManager().dispatchCommand(player, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -59,7 +46,7 @@ public class BungeePlayer implements IPlayer {
|
||||
|
||||
@Override
|
||||
public @NotNull String username() {
|
||||
return player.getName();
|
||||
return commandSender.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -72,8 +59,4 @@ public class BungeePlayer implements IPlayer {
|
||||
return BungeeComponentUtil.fromLegacy(player.getDisplayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return audience;
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,9 @@ dependencies {
|
||||
runtimeDownloadApi 'dev.vankka:mcdiscordreserializer:4.2.4-SNAPSHOT'
|
||||
runtimeDownloadApi 'dev.vankka:enhancedlegacytext:1.0.0-SNAPSHOT'
|
||||
|
||||
// Brigadier
|
||||
compileOnlyApi 'com.mojang:brigadier:1.0.18'
|
||||
|
||||
// Database
|
||||
hikari('com.zaxxer:HikariCP:4.0.3') { exclude group: 'org.slf4j' }
|
||||
h2Driver 'com.h2database:h2:2.1.210'
|
||||
|
@ -20,13 +20,16 @@ package com.discordsrv.common;
|
||||
|
||||
import com.discordsrv.api.discord.connection.DiscordConnectionDetails;
|
||||
import com.discordsrv.api.event.bus.EventBus;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVConnectedEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReadyEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadedEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVShuttingDownEvent;
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
import com.discordsrv.common.api.util.ApiInstanceUtil;
|
||||
import com.discordsrv.common.channel.ChannelConfigHelper;
|
||||
import com.discordsrv.common.channel.ChannelUpdaterModule;
|
||||
import com.discordsrv.common.channel.GlobalChannelLookupModule;
|
||||
import com.discordsrv.common.command.game.GameCommandModule;
|
||||
import com.discordsrv.common.component.ComponentFactory;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.LinkedAccountConfig;
|
||||
@ -45,6 +48,7 @@ import com.discordsrv.common.function.CheckedFunction;
|
||||
import com.discordsrv.common.function.CheckedRunnable;
|
||||
import com.discordsrv.common.groupsync.GroupSyncModule;
|
||||
import com.discordsrv.common.integration.LuckPermsIntegration;
|
||||
import com.discordsrv.common.invite.DiscordInviteModule;
|
||||
import com.discordsrv.common.linking.LinkProvider;
|
||||
import com.discordsrv.common.linking.impl.MemoryLinker;
|
||||
import com.discordsrv.common.linking.impl.StorageLinker;
|
||||
@ -69,6 +73,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import javax.annotation.OverridingMethodsMustInvokeSuper;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -96,11 +101,12 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
// DiscordSRV
|
||||
private DiscordSRVLogger logger;
|
||||
private ModuleManager moduleManager;
|
||||
private JDAConnectionManager discordConnectionManager;
|
||||
private ChannelConfigHelper channelConfig;
|
||||
|
||||
private Storage storage;
|
||||
private boolean hikariLoaded = false;
|
||||
private LinkProvider linkProvider;
|
||||
private DiscordConnectionManager discordConnectionManager;
|
||||
|
||||
// Internal
|
||||
private final ReentrantLock lifecycleLock = new ReentrantLock();
|
||||
@ -121,6 +127,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
this.componentFactory = new ComponentFactory(this);
|
||||
this.discordAPI = new DiscordAPIImpl(this);
|
||||
this.discordConnectionDetails = new DiscordConnectionDetailsImpl(this);
|
||||
this.discordConnectionManager = new JDAConnectionManager(this);
|
||||
this.channelConfig = new ChannelConfigHelper(this);
|
||||
}
|
||||
|
||||
@ -225,9 +232,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends DiscordSRV> void registerModule(CheckedFunction<T, AbstractModule<?>> function) {
|
||||
try {
|
||||
registerModule(function.apply((T) this));
|
||||
} catch (Throwable ignored) {}
|
||||
moduleManager.registerModule((T) this, function);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -249,6 +254,9 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
this.status.set(status);
|
||||
this.status.notifyAll();
|
||||
}
|
||||
if (status == Status.CONNECTED) {
|
||||
eventBus().publish(new DiscordSRVConnectedEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -299,7 +307,11 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
|
||||
@Override
|
||||
public final CompletableFuture<Void> invokeEnable() {
|
||||
return invokeLifecycle(this::enable, "Failed to enable", true);
|
||||
return invokeLifecycle(() -> {
|
||||
this.enable();
|
||||
waitForStatus(Status.CONNECTED);
|
||||
eventBus().publish(new DiscordSRVReadyEvent());
|
||||
}, "Failed to enable", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -308,8 +320,8 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
}
|
||||
|
||||
@Override
|
||||
public final CompletableFuture<Void> invokeReload() {
|
||||
return invoke(this::reload, "Failed to reload", false);
|
||||
public final CompletableFuture<Void> invokeReload(Set<ReloadFlag> flags, boolean silent) {
|
||||
return invoke(() -> reload(flags, silent), "Failed to reload", false);
|
||||
}
|
||||
|
||||
@OverridingMethodsMustInvokeSuper
|
||||
@ -323,76 +335,16 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
// Logging
|
||||
DependencyLoggerAdapter.setAppender(new DependencyLoggingHandler(this));
|
||||
|
||||
// Config
|
||||
try {
|
||||
connectionConfigManager().load();
|
||||
configManager().load();
|
||||
eventBus().publish(new DiscordSRVReloadEvent(true));
|
||||
} catch (Throwable t) {
|
||||
setStatus(Status.FAILED_TO_LOAD_CONFIG);
|
||||
throw t;
|
||||
}
|
||||
|
||||
// Link provider
|
||||
LinkedAccountConfig linkedAccountConfig = config().linkedAccounts;
|
||||
if (linkedAccountConfig != null && linkedAccountConfig.enabled) {
|
||||
String provider = linkedAccountConfig.provider;
|
||||
switch (provider) {
|
||||
case "auto":
|
||||
case "storage":
|
||||
linkProvider = new StorageLinker(this);
|
||||
break;
|
||||
case "memory": {
|
||||
linkProvider = new MemoryLinker();
|
||||
logger().warning("Using memory for linked accounts");
|
||||
logger().warning("Linked accounts will be lost upon restart");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
logger().error("Unknown linked account provider: \"" + provider + "\", linked accounts will not be used");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger().info("Linked accounts are disabled");
|
||||
}
|
||||
|
||||
// Storage
|
||||
try {
|
||||
try {
|
||||
StorageType storageType = getStorageType();
|
||||
logger().info("Using " + storageType.prettyName() + " as storage");
|
||||
if (storageType.hikari()) {
|
||||
DependencyLoader.hikari(this).process(classpathAppender()).get();
|
||||
}
|
||||
storage = storageType.storageFunction().apply(this);
|
||||
storage.initialize();
|
||||
logger().info("Storage connection successfully established");
|
||||
} catch (ExecutionException e) {
|
||||
throw new StorageException(e.getCause());
|
||||
} catch (StorageException e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
throw new StorageException(t);
|
||||
}
|
||||
} catch (StorageException e) {
|
||||
e.log(this);
|
||||
logger().error("Failed to connect to storage");
|
||||
setStatus(Status.FAILED_TO_START);
|
||||
return;
|
||||
}
|
||||
|
||||
discordConnectionManager = new JDAConnectionManager(this);
|
||||
discordConnectionManager.connect().join();
|
||||
// Register PlayerProvider listeners
|
||||
playerProvider().subscribe();
|
||||
|
||||
// Placeholder result stringifiers & global contexts
|
||||
placeholderService().addResultMapper(new ComponentResultStringifier(this));
|
||||
placeholderService().addGlobalContext(new GlobalTextHandlingContext(this));
|
||||
|
||||
// Register PlayerProvider listeners
|
||||
playerProvider().subscribe();
|
||||
|
||||
// Modules
|
||||
registerModule(ChannelUpdaterModule::new);
|
||||
registerModule(GameCommandModule::new);
|
||||
registerModule(GlobalChannelLookupModule::new);
|
||||
registerModule(DiscordAPIEventModule::new);
|
||||
registerModule(GroupSyncModule::new);
|
||||
@ -401,6 +353,14 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
registerModule(DiscordMessageMirroringModule::new);
|
||||
registerModule(JoinMessageModule::new);
|
||||
registerModule(LeaveMessageModule::new);
|
||||
registerModule(DiscordInviteModule::new);
|
||||
|
||||
// Initial load
|
||||
try {
|
||||
invokeReload(ReloadFlag.ALL, true).get();
|
||||
} catch (ExecutionException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
private StorageType getStorageType() {
|
||||
@ -425,7 +385,102 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
}
|
||||
|
||||
@OverridingMethodsMustInvokeSuper
|
||||
protected void reload() {
|
||||
protected void reload(Set<ReloadFlag> flags, boolean initial) throws Throwable {
|
||||
if (!initial) {
|
||||
logger().info("Reloading DiscordSRV...");
|
||||
}
|
||||
|
||||
if (flags.contains(ReloadFlag.CONFIG)) {
|
||||
try {
|
||||
connectionConfigManager().load();
|
||||
configManager().load();
|
||||
} catch (Throwable t) {
|
||||
setStatus(Status.FAILED_TO_LOAD_CONFIG);
|
||||
throw t;
|
||||
}
|
||||
|
||||
channelConfig().reload();
|
||||
}
|
||||
|
||||
if (flags.contains(ReloadFlag.LINKED_ACCOUNT_PROVIDER)) {
|
||||
LinkedAccountConfig linkedAccountConfig = config().linkedAccounts;
|
||||
if (linkedAccountConfig != null && linkedAccountConfig.enabled) {
|
||||
String provider = linkedAccountConfig.provider;
|
||||
switch (provider) {
|
||||
case "auto":
|
||||
case "storage":
|
||||
linkProvider = new StorageLinker(this);
|
||||
logger().info("Using storage for linked accounts");
|
||||
break;
|
||||
case "memory": {
|
||||
linkProvider = new MemoryLinker();
|
||||
logger().warning("Using memory for linked accounts");
|
||||
logger().warning("Linked accounts will be lost upon restart");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
linkProvider = null;
|
||||
logger().error("Unknown linked account provider: \"" + provider + "\", linked accounts will not be used");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
linkProvider = null;
|
||||
logger().info("Linked accounts are disabled");
|
||||
}
|
||||
}
|
||||
|
||||
if (flags.contains(ReloadFlag.STORAGE)) {
|
||||
if (storage != null) {
|
||||
storage.close();
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
StorageType storageType = getStorageType();
|
||||
logger().info("Using " + storageType.prettyName() + " as storage");
|
||||
if (storageType.hikari() && !hikariLoaded) {
|
||||
hikariLoaded = true;
|
||||
DependencyLoader.hikari(this).process(classpathAppender()).get();
|
||||
}
|
||||
storage = storageType.storageFunction().apply(this);
|
||||
storage.initialize();
|
||||
logger().info("Storage connection successfully established");
|
||||
} catch (ExecutionException e) {
|
||||
throw new StorageException(e.getCause());
|
||||
} catch (StorageException e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
throw new StorageException(t);
|
||||
}
|
||||
} catch (StorageException e) {
|
||||
e.log(this);
|
||||
logger().error("Failed to connect to storage");
|
||||
setStatus(Status.FAILED_TO_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags.contains(ReloadFlag.DISCORD_CONNECTION)) {
|
||||
try {
|
||||
if (discordConnectionManager.instance() != null) {
|
||||
discordConnectionManager.reconnect().get();
|
||||
} else {
|
||||
discordConnectionManager.connect().get();
|
||||
}
|
||||
waitForStatus(Status.CONNECTED, 20, TimeUnit.SECONDS);
|
||||
} catch (ExecutionException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
if (flags.contains(ReloadFlag.MODULES)) {
|
||||
moduleManager.reload();
|
||||
}
|
||||
|
||||
if (!initial) {
|
||||
eventBus().publish(new DiscordSRVReloadedEvent(flags));
|
||||
logger().info("Reload complete.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ package com.discordsrv.common;
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
import com.discordsrv.common.channel.ChannelConfigHelper;
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.component.ComponentFactory;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.MainConfig;
|
||||
@ -47,7 +48,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -62,6 +63,7 @@ public interface DiscordSRV extends DiscordSRVApi {
|
||||
PluginManager pluginManager();
|
||||
OnlineMode onlineMode();
|
||||
ClasspathAppender classpathAppender();
|
||||
ICommandHandler commandHandler();
|
||||
@NotNull AbstractPlayerProvider<?, ?> playerProvider();
|
||||
|
||||
// DiscordSRVApi
|
||||
@ -126,6 +128,6 @@ public interface DiscordSRV extends DiscordSRVApi {
|
||||
// Lifecycle
|
||||
CompletableFuture<Void> invokeEnable();
|
||||
CompletableFuture<Void> invokeDisable();
|
||||
CompletableFuture<Void> invokeReload();
|
||||
CompletableFuture<Void> invokeReload(Set<ReloadFlag> flags, boolean silent);
|
||||
|
||||
}
|
||||
|
@ -22,9 +22,7 @@ import com.discordsrv.api.channel.GameChannel;
|
||||
import com.discordsrv.api.discord.api.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel;
|
||||
import com.discordsrv.api.discord.api.entity.channel.DiscordThreadChannel;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.channel.GameChannelLookupEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadEvent;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.base.ChannelConfig;
|
||||
@ -94,16 +92,9 @@ public class ChannelConfigHelper {
|
||||
}
|
||||
return map;
|
||||
});
|
||||
|
||||
discordSRV.eventBus().subscribe(this);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onReload(DiscordSRVReloadEvent event) {
|
||||
if (!event.isConfig()) {
|
||||
return;
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
Map<Long, Map<String, BaseChannelConfig>> newMap = new HashMap<>();
|
||||
for (Map.Entry<String, BaseChannelConfig> entry : channels().entrySet()) {
|
||||
String channelName = entry.getKey();
|
||||
|
@ -115,5 +115,4 @@ public class ChannelUpdaterModule extends AbstractModule<DiscordSRV> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.common.command.game;
|
||||
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.command.DiscordSRVCommand;
|
||||
import com.discordsrv.common.command.game.command.subcommand.LinkCommand;
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.config.main.CommandConfig;
|
||||
import com.discordsrv.common.module.type.AbstractModule;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class GameCommandModule extends AbstractModule<DiscordSRV> {
|
||||
|
||||
private final Set<GameCommand> commands = new HashSet<>();
|
||||
|
||||
private final GameCommand primaryCommand;
|
||||
private final GameCommand discordAlias;
|
||||
private final GameCommand linkCommand;
|
||||
|
||||
public GameCommandModule(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
this.primaryCommand = DiscordSRVCommand.get(discordSRV);
|
||||
this.discordAlias = GameCommand.literal("discord").redirect(primaryCommand);
|
||||
this.linkCommand = LinkCommand.get(discordSRV);
|
||||
|
||||
registerCommand(primaryCommand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
CommandConfig config = discordSRV.config().command;
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
registerCommand(primaryCommand);
|
||||
if (config.useDiscordCommand) {
|
||||
registerCommand(discordAlias);
|
||||
}
|
||||
if (config.useLinkAlias) {
|
||||
registerCommand(linkCommand);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCommand(GameCommand command) {
|
||||
ICommandHandler handler = discordSRV.commandHandler();
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!commands.add(command)) {
|
||||
return;
|
||||
}
|
||||
|
||||
handler.registerCommand(command);
|
||||
}
|
||||
}
|
@ -0,0 +1,473 @@
|
||||
/*
|
||||
* 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.common.command.game.abstraction;
|
||||
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import com.discordsrv.common.function.CheckedFunction;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class GameCommand {
|
||||
|
||||
private static final GameCommandSuggester BOOLEANS_SUGGESTER = (player, previous, current) -> {
|
||||
if (current.isEmpty()) {
|
||||
return Arrays.asList("true", "false");
|
||||
} else if ("true".startsWith(current)) {
|
||||
return Collections.singletonList("true");
|
||||
} else if ("false".startsWith(current)) {
|
||||
return Collections.singletonList("false");
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
};
|
||||
|
||||
public static GameCommand literal(String label) {
|
||||
return new GameCommand(label, ArgumentType.LITERAL);
|
||||
}
|
||||
|
||||
public static GameCommand booleanArgument(String label) {
|
||||
return new GameCommand(label, ArgumentType.BOOLEAN).suggester(BOOLEANS_SUGGESTER);
|
||||
}
|
||||
|
||||
public static GameCommand doubleArgument(String label) {
|
||||
return new GameCommand(label, ArgumentType.DOUBLE);
|
||||
}
|
||||
|
||||
public static GameCommand floatArgument(String label) {
|
||||
return new GameCommand(label, ArgumentType.FLOAT);
|
||||
}
|
||||
|
||||
public static GameCommand integerArgument(String label) {
|
||||
return new GameCommand(label, ArgumentType.INTEGER);
|
||||
}
|
||||
|
||||
public static GameCommand longArgument(String label) {
|
||||
return new GameCommand(label, ArgumentType.LONG);
|
||||
}
|
||||
|
||||
public static GameCommand string(String label) {
|
||||
return new GameCommand(label, ArgumentType.STRING);
|
||||
}
|
||||
|
||||
public static GameCommand stringWord(String label) {
|
||||
return new GameCommand(label, ArgumentType.STRING_WORD);
|
||||
}
|
||||
|
||||
public static GameCommand stringGreedy(String label) {
|
||||
return new GameCommand(label, ArgumentType.STRING_GREEDY);
|
||||
}
|
||||
|
||||
private final ExecutorProxy executorProxy = new ExecutorProxy();
|
||||
private final SuggesterProxy suggesterProxy = new SuggesterProxy();
|
||||
|
||||
// Command info
|
||||
private GameCommand parent = null;
|
||||
private final String label;
|
||||
private final ArgumentType argumentType;
|
||||
private final List<GameCommand> children;
|
||||
private GameCommand redirection = null;
|
||||
|
||||
// Permission
|
||||
private String requiredPermission;
|
||||
private Component noPermissionMessage = null;
|
||||
|
||||
// Executor & suggestor
|
||||
private GameCommandExecutor commandExecutor = null;
|
||||
private GameCommandSuggester commandSuggester = null;
|
||||
|
||||
// Argument type bounds
|
||||
private double maxValue = Double.MAX_VALUE;
|
||||
private double minValue = Double.MIN_VALUE;
|
||||
|
||||
private GameCommand(String label, ArgumentType argumentType) {
|
||||
this.label = label;
|
||||
this.argumentType = argumentType;
|
||||
this.children = new ArrayList<>();
|
||||
}
|
||||
|
||||
private GameCommand(GameCommand original) {
|
||||
this.parent = original.parent;
|
||||
this.label = original.label;
|
||||
this.argumentType = original.argumentType;
|
||||
this.children = original.children;
|
||||
this.redirection = original.redirection;
|
||||
this.requiredPermission = original.requiredPermission;
|
||||
this.noPermissionMessage = original.noPermissionMessage;
|
||||
this.commandExecutor = original.commandExecutor;
|
||||
this.commandSuggester = original.commandSuggester;
|
||||
this.maxValue = original.maxValue;
|
||||
this.minValue = original.minValue;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public ArgumentType getArgumentType() {
|
||||
return argumentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a sub command. You can only have multiple literals (with unique labels) or a single non-literal one.
|
||||
*/
|
||||
public GameCommand then(GameCommand child) {
|
||||
if (redirection != null) {
|
||||
throw new IllegalStateException("Cannot add children to a redirected node");
|
||||
}
|
||||
for (GameCommand builder : children) {
|
||||
if (builder.getArgumentType() == ArgumentType.LITERAL
|
||||
&& child.getArgumentType() == ArgumentType.LITERAL
|
||||
&& builder.getLabel().equals(child.getLabel())) {
|
||||
throw new IllegalArgumentException("Duplicate literal with label \"" + child.label + "\"");
|
||||
}
|
||||
if (child.getArgumentType() == ArgumentType.LITERAL && builder.getArgumentType() != ArgumentType.LITERAL) {
|
||||
throw new IllegalStateException("A non-literal is already present, cannot add literal");
|
||||
}
|
||||
if (child.getArgumentType() != ArgumentType.LITERAL) {
|
||||
throw new IllegalStateException("Cannot add non-literal when another child is already present");
|
||||
}
|
||||
}
|
||||
if (child.getNoPermissionMessage() == null && noPermissionMessage != null) {
|
||||
child.noPermissionMessage(noPermissionMessage);
|
||||
}
|
||||
child.parent = this;
|
||||
this.children.add(child);
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<GameCommand> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public GameCommand redirect(GameCommand redirection) {
|
||||
if (!children.isEmpty()) {
|
||||
throw new IllegalStateException("Cannot redirect a node with children");
|
||||
}
|
||||
if (requiredPermission != null) {
|
||||
throw new IllegalStateException("Cannot redirect a node with a required permission");
|
||||
}
|
||||
this.redirection = redirection;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GameCommand getRedirection() {
|
||||
return redirection;
|
||||
}
|
||||
|
||||
public GameCommand requiredPermission(String permission) {
|
||||
if (redirection != null) {
|
||||
throw new IllegalStateException("Cannot required permissions on a node with a redirection");
|
||||
}
|
||||
this.requiredPermission = permission;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRequiredPermission() {
|
||||
if (redirection != null) {
|
||||
return redirection.getRequiredPermission();
|
||||
}
|
||||
return requiredPermission;
|
||||
}
|
||||
|
||||
public GameCommand noPermissionMessage(Component noPermissionMessage) {
|
||||
this.noPermissionMessage = noPermissionMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Component getNoPermissionMessage() {
|
||||
return noPermissionMessage;
|
||||
}
|
||||
|
||||
public GameCommand executor(GameCommandExecutor executor) {
|
||||
this.commandExecutor = executor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GameCommandExecutor getExecutor() {
|
||||
return executorProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cannot be used with {@link #literal(String)}.
|
||||
*/
|
||||
public GameCommand suggester(GameCommandSuggester suggester) {
|
||||
if (argumentType == ArgumentType.LITERAL) {
|
||||
throw new IllegalArgumentException("Cannot use on argument type literal");
|
||||
}
|
||||
this.commandSuggester = suggester;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GameCommandSuggester getSuggester() {
|
||||
return suggesterProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can only be used on number argument types.
|
||||
*/
|
||||
public GameCommand minValue(double minValue) {
|
||||
mustBeNumber();
|
||||
this.minValue = minValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getMinValue() {
|
||||
return minValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can only be used on number argument types.
|
||||
*/
|
||||
public GameCommand maxValue(double maxValue) {
|
||||
mustBeNumber();
|
||||
this.maxValue = maxValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
private void mustBeNumber() {
|
||||
if (!argumentType.number()) {
|
||||
throw new IllegalArgumentException("Cannot be used on this argument type");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPermission(ICommandSender sender) {
|
||||
String requiredPermission = getRequiredPermission();
|
||||
return requiredPermission == null || sender.hasPermission(requiredPermission);
|
||||
}
|
||||
|
||||
public void sendNoPermission(ICommandSender sender) {
|
||||
sender.sendMessage(noPermissionMessage != null
|
||||
? noPermissionMessage
|
||||
: Component.text("No permission", NamedTextColor.RED));
|
||||
}
|
||||
|
||||
public ArgumentResult checkArgument(String argument) {
|
||||
switch (getArgumentType()) {
|
||||
case LITERAL: return ArgumentResult.fromBoolean(argument.equals(getLabel()));
|
||||
case STRING: {
|
||||
if (argument.startsWith("\"")) {
|
||||
boolean single = argument.length() == 1;
|
||||
if (argument.endsWith("\"") && !single) {
|
||||
return new ArgumentResult(argument.substring(1, argument.length() - 1), MatchResult.MATCHES);
|
||||
}
|
||||
return new ArgumentResult(single ? "" : argument.substring(1), single ? MatchResult.END : MatchResult.CONTINUE);
|
||||
}
|
||||
if (argument.endsWith("\"")) {
|
||||
return new ArgumentResult(argument.substring(0, argument.length() - 1), MatchResult.END);
|
||||
}
|
||||
return new ArgumentResult(argument, MatchResult.MATCHES);
|
||||
}
|
||||
case STRING_GREEDY: return new ArgumentResult(argument, GameCommand.MatchResult.CONTINUE);
|
||||
default: {
|
||||
try {
|
||||
return new ArgumentResult(getArgumentType().function().apply(argument), MatchResult.MATCHES);
|
||||
} catch (Throwable ignored) {
|
||||
return ArgumentResult.NO_MATCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getArgumentLabel() {
|
||||
if (argumentType == ArgumentType.LITERAL) {
|
||||
return label;
|
||||
} else {
|
||||
return "<" + label + ">";
|
||||
}
|
||||
}
|
||||
|
||||
public Component describe() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
GameCommand current = this;
|
||||
while (current != null) {
|
||||
stringBuilder.insert(0, current.getArgumentLabel() + " ");
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
String command = "/" + stringBuilder.substring(1);
|
||||
return Component.text(command, NamedTextColor.AQUA);
|
||||
}
|
||||
|
||||
public void sendCommandInstructions(ICommandSender sender) {
|
||||
if (children.isEmpty()) {
|
||||
throw new IllegalStateException("No children");
|
||||
}
|
||||
|
||||
TextComponent.Builder builder = Component.text();
|
||||
builder.append(describe().color(NamedTextColor.GRAY).append(Component.text(" available subcommands:")));
|
||||
boolean anyAvailable = false;
|
||||
for (GameCommand child : children) {
|
||||
if (!child.hasPermission(sender)) {
|
||||
continue;
|
||||
}
|
||||
anyAvailable = true;
|
||||
builder.append(Component.newline()).append(child.describe());
|
||||
}
|
||||
if (!anyAvailable) {
|
||||
builder.append(Component.newline())
|
||||
.append(Component.text("No available subcommands", NamedTextColor.RED));
|
||||
}
|
||||
sender.sendMessage(builder.build());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException", "MethodDoesntCallSuperMethod"})
|
||||
@Override
|
||||
protected GameCommand clone() {
|
||||
return new GameCommand(this);
|
||||
}
|
||||
|
||||
public static class ArgumentResult {
|
||||
|
||||
public static final ArgumentResult EMPTY_MATCH = new ArgumentResult(MatchResult.MATCHES);
|
||||
public static final ArgumentResult NO_MATCH = new ArgumentResult(MatchResult.NO_MATCH);
|
||||
|
||||
public static ArgumentResult fromBoolean(boolean value) {
|
||||
return value ? EMPTY_MATCH : NO_MATCH;
|
||||
}
|
||||
|
||||
private final Object value;
|
||||
private final MatchResult result;
|
||||
|
||||
private ArgumentResult(MatchResult result) {
|
||||
this(null, result);
|
||||
}
|
||||
|
||||
public ArgumentResult(Object value, MatchResult result) {
|
||||
this.value = value;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public Object value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public MatchResult result() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MatchResult {
|
||||
MATCHES,
|
||||
CONTINUE,
|
||||
END,
|
||||
NO_MATCH
|
||||
}
|
||||
|
||||
public enum ArgumentType {
|
||||
|
||||
LITERAL(),
|
||||
BOOLEAN(false, input -> {
|
||||
boolean value;
|
||||
if ((value = input.equals("true")) || input.equals("false")) {
|
||||
return value;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}),
|
||||
DOUBLE(true, Double::parseDouble),
|
||||
FLOAT(true, Float::parseFloat),
|
||||
INTEGER(true, Integer::parseInt),
|
||||
LONG(true, Long::parseLong),
|
||||
STRING_WORD(false, input -> input),
|
||||
STRING(false, true),
|
||||
STRING_GREEDY(false, true);
|
||||
|
||||
private final boolean number;
|
||||
private final boolean multi;
|
||||
private final CheckedFunction<String, Object> function;
|
||||
|
||||
ArgumentType() {
|
||||
this(false, null);
|
||||
}
|
||||
|
||||
ArgumentType(boolean number, CheckedFunction<String, Object> function) {
|
||||
this(number, false, function);
|
||||
}
|
||||
|
||||
ArgumentType(boolean number, boolean multi) {
|
||||
this(number, multi, null);
|
||||
}
|
||||
|
||||
ArgumentType(boolean number, boolean multi, CheckedFunction<String, Object> function) {
|
||||
this.number = number;
|
||||
this.multi = multi;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public boolean number() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public boolean multi() {
|
||||
return multi;
|
||||
}
|
||||
|
||||
public CheckedFunction<String, Object> function() {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
private class ExecutorProxy implements GameCommandExecutor {
|
||||
|
||||
@Override
|
||||
public void execute(ICommandSender sender, GameCommandArguments arguments) {
|
||||
if (!hasPermission(sender)) {
|
||||
sendNoPermission(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (commandExecutor != null) {
|
||||
commandExecutor.execute(sender, arguments);
|
||||
} else if (!children.isEmpty()) {
|
||||
sendCommandInstructions(sender);
|
||||
} else {
|
||||
throw new IllegalStateException("Command (" + GameCommand.this + ") doesn't have children and has no executor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SuggesterProxy implements GameCommandSuggester {
|
||||
|
||||
@Override
|
||||
public List<String> suggestValues(
|
||||
ICommandSender sender,
|
||||
GameCommandArguments previousArguments,
|
||||
String currentInput
|
||||
) {
|
||||
if (!hasPermission(sender)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (commandSuggester != null) {
|
||||
return commandSuggester.suggestValues(sender, previousArguments, currentInput);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.common.command.game.abstraction;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface GameCommandArguments {
|
||||
|
||||
<T> T get(String label, Class<T> type);
|
||||
|
||||
default Integer getInt(String label) {
|
||||
return get(label, Integer.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.common.command.game.abstraction;
|
||||
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface GameCommandExecutor {
|
||||
|
||||
void execute(ICommandSender sender, GameCommandArguments arguments);
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.common.command.game.abstraction;
|
||||
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface GameCommandSuggester {
|
||||
|
||||
List<String> suggestValues(ICommandSender sender, GameCommandArguments previousArguments, String currentInput);
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.common.command.game.command;
|
||||
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
|
||||
import com.discordsrv.common.command.game.command.subcommand.LinkCommand;
|
||||
import com.discordsrv.common.command.game.command.subcommand.ReloadCommand;
|
||||
import com.discordsrv.common.command.game.command.subcommand.VersionCommand;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
|
||||
public class DiscordSRVCommand implements GameCommandExecutor {
|
||||
|
||||
private static GameCommand INSTANCE;
|
||||
|
||||
public static GameCommand get(DiscordSRV discordSRV) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = GameCommand.literal("discordsrv")
|
||||
.requiredPermission("discordsrv.player.command")
|
||||
.executor(new DiscordSRVCommand(discordSRV))
|
||||
.then(LinkCommand.get(discordSRV))
|
||||
.then(ReloadCommand.get(discordSRV))
|
||||
.then(VersionCommand.get(discordSRV));
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public DiscordSRVCommand(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ICommandSender sender, GameCommandArguments arguments) {
|
||||
MinecraftComponent component = discordSRV.componentFactory()
|
||||
.enhancedBuilder(discordSRV.config().command.discordFormat)
|
||||
.addContext(sender)
|
||||
.applyPlaceholderService()
|
||||
.build();
|
||||
|
||||
sender.sendMessage(ComponentUtil.fromAPI(component));
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.common.command.game.command.subcommand;
|
||||
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
|
||||
public class LinkCommand implements GameCommandExecutor {
|
||||
|
||||
private static GameCommand INSTANCE;
|
||||
|
||||
public static GameCommand get(DiscordSRV discordSRV) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = GameCommand.literal("link")
|
||||
.requiredPermission("discordsrv.player.link")
|
||||
.executor(new LinkCommand(discordSRV));
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public LinkCommand(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ICommandSender sender, GameCommandArguments arguments) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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.common.command.game.command.subcommand;
|
||||
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandSuggester;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ReloadCommand implements GameCommandExecutor, GameCommandSuggester {
|
||||
|
||||
private static GameCommand INSTANCE;
|
||||
|
||||
public static GameCommand get(DiscordSRV discordSRV) {
|
||||
if (INSTANCE == null) {
|
||||
ReloadCommand cmd = new ReloadCommand(discordSRV);
|
||||
INSTANCE = GameCommand.literal("reload")
|
||||
.requiredPermission("discordsrv.admin.reload")
|
||||
.executor(cmd)
|
||||
.then(
|
||||
GameCommand.stringGreedy("flags")
|
||||
.executor(cmd).suggester(cmd)
|
||||
);
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public ReloadCommand(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ICommandSender sender, GameCommandArguments arguments) {
|
||||
AtomicBoolean dangerousFlags = new AtomicBoolean(false);
|
||||
Set<DiscordSRV.ReloadFlag> flags = getFlagsFromArguments(sender, arguments, dangerousFlags);
|
||||
if (flags == null) {
|
||||
flags = DiscordSRV.ReloadFlag.DEFAULT_FLAGS;
|
||||
}
|
||||
|
||||
if (dangerousFlags.get()) {
|
||||
sender.sendMessage(Component.text("You can add -confirm to the end of the command if you wish to proceed anyway", NamedTextColor.DARK_RED));
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags.isEmpty()) {
|
||||
sender.sendMessage(Component.text("Please specify at least one valid flag", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
discordSRV.invokeReload(flags, false).whenComplete((v, t) -> {
|
||||
if (t != null) {
|
||||
discordSRV.logger().error("Failed to reload", t);
|
||||
sender.sendMessage(
|
||||
Component.text()
|
||||
.append(Component.text("Reload failed.", NamedTextColor.DARK_RED, TextDecoration.BOLD))
|
||||
.append(Component.text("Please check the server console/log for more details."))
|
||||
);
|
||||
} else {
|
||||
sender.sendMessage(Component.text("Reload successful", NamedTextColor.GRAY));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<DiscordSRV.ReloadFlag> getFlagsFromArguments(ICommandSender sender, GameCommandArguments arguments, AtomicBoolean dangerousFlags) {
|
||||
String argument = null;
|
||||
try {
|
||||
argument = arguments.get("flags", String.class);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
|
||||
if (argument == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> parts = new ArrayList<>(Arrays.asList(argument.split(" ")));
|
||||
boolean confirm = parts.remove("-confirm");
|
||||
|
||||
Set<DiscordSRV.ReloadFlag> flags = new LinkedHashSet<>();
|
||||
for (String part : parts) {
|
||||
try {
|
||||
DiscordSRV.ReloadFlag flag = DiscordSRV.ReloadFlag.valueOf(part.toUpperCase(Locale.ROOT));
|
||||
if (flag.requiresConfirm() && !confirm) {
|
||||
dangerousFlags.set(true);
|
||||
sender.sendMessage(
|
||||
Component.text("Reloading ", NamedTextColor.RED)
|
||||
.append(Component.text(part, NamedTextColor.GRAY))
|
||||
.append(Component.text(" might cause DiscordSRV to end up in a unrecoverable state", NamedTextColor.RED))
|
||||
);
|
||||
continue;
|
||||
}
|
||||
flags.add(flag);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
sender.sendMessage(Component.text("Flag ", NamedTextColor.RED)
|
||||
.append(Component.text(part, NamedTextColor.GRAY))
|
||||
.append(Component.text(" is not known", NamedTextColor.RED)));
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestValues(
|
||||
ICommandSender sender,
|
||||
GameCommandArguments previousArguments,
|
||||
String currentInput
|
||||
) {
|
||||
int lastSpace = currentInput.lastIndexOf(' ') + 1;
|
||||
String last = currentInput.substring(lastSpace);
|
||||
String beforeLastSpace = currentInput.substring(0, lastSpace);
|
||||
|
||||
List<String> options = DiscordSRV.ReloadFlag.ALL.stream()
|
||||
.map(flag -> flag.name().toLowerCase(Locale.ROOT))
|
||||
.filter(flag -> flag.startsWith(last))
|
||||
.collect(Collectors.toList());
|
||||
for (String part : currentInput.split(" ")) {
|
||||
options.remove(part);
|
||||
}
|
||||
|
||||
return options.stream().map(flag -> beforeLastSpace + flag).collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.common.command.game.command.subcommand;
|
||||
|
||||
import com.discordsrv.api.color.Color;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
|
||||
public class VersionCommand implements GameCommandExecutor {
|
||||
|
||||
private static GameCommand INSTANCE;
|
||||
|
||||
public static GameCommand get(DiscordSRV discordSRV) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = GameCommand.literal("version")
|
||||
.requiredPermission("discordsrv.admin.version")
|
||||
.executor(new VersionCommand(discordSRV));
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public VersionCommand(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ICommandSender sender, GameCommandArguments arguments) {
|
||||
sender.sendMessage(
|
||||
Component.text().content("Running DiscordSRV ").color(TextColor.color(Color.BLURPLE.rgb()))
|
||||
.append(Component.text("v" + discordSRV.version(), NamedTextColor.GRAY))
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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.common.command.game.handler;
|
||||
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BasicCommandHandler implements ICommandHandler {
|
||||
|
||||
private final Map<String, GameCommand> commands = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void registerCommand(GameCommand command) {
|
||||
commands.put(command.getLabel(), command);
|
||||
}
|
||||
|
||||
private GameCommand processCommand(
|
||||
GameCommand command,
|
||||
List<String> arguments,
|
||||
Arguments argumentValues,
|
||||
BiConsumer<GameCommand, Arguments> tooManyArguments,
|
||||
BiConsumer<GameCommand, Arguments> unclosedQuote,
|
||||
List<String> literalOptions
|
||||
) {
|
||||
GameCommand redirection = command.getRedirection();
|
||||
if (redirection != null) {
|
||||
command = redirection;
|
||||
}
|
||||
|
||||
if (!arguments.isEmpty()) {
|
||||
List<String> currentOptions = new ArrayList<>();
|
||||
for (GameCommand child : command.getChildren()) {
|
||||
if (child.getArgumentType() == GameCommand.ArgumentType.LITERAL) {
|
||||
currentOptions.add(child.getLabel());
|
||||
}
|
||||
|
||||
GameCommand.ArgumentResult result = child.checkArgument(arguments.get(0));
|
||||
GameCommand.MatchResult matchResult = result.result();
|
||||
if (matchResult == GameCommand.MatchResult.NO_MATCH) {
|
||||
continue;
|
||||
}
|
||||
arguments.remove(0);
|
||||
|
||||
Object value;
|
||||
if (child.getArgumentType().multi()) {
|
||||
StringBuilder content = new StringBuilder((String) result.value());
|
||||
if (matchResult == GameCommand.MatchResult.END) {
|
||||
// Premature end
|
||||
if (unclosedQuote != null) {
|
||||
unclosedQuote.accept(command, argumentValues);
|
||||
}
|
||||
return unclosedQuote == null ? command : null;
|
||||
}
|
||||
|
||||
boolean stringStart = matchResult == GameCommand.MatchResult.CONTINUE;
|
||||
while (stringStart && !arguments.isEmpty()) {
|
||||
result = child.checkArgument(arguments.remove(0));
|
||||
Object resultValue = result.value();
|
||||
content.append(' ').append(resultValue != null ? resultValue : "");
|
||||
matchResult = result.result();
|
||||
if (matchResult == GameCommand.MatchResult.END) {
|
||||
stringStart = false;
|
||||
}
|
||||
}
|
||||
if (stringStart && child.getArgumentType() == GameCommand.ArgumentType.STRING) {
|
||||
// Unclosed quote
|
||||
if (unclosedQuote != null) {
|
||||
unclosedQuote.accept(command, argumentValues);
|
||||
} else {
|
||||
argumentValues.put(child.getLabel(), "\"" + content);
|
||||
}
|
||||
return unclosedQuote == null ? child : null;
|
||||
}
|
||||
|
||||
value = content.toString();
|
||||
} else {
|
||||
value = result.value();
|
||||
}
|
||||
|
||||
argumentValues.put(child.getLabel(), value);
|
||||
return processCommand(child, arguments, argumentValues, tooManyArguments, unclosedQuote, literalOptions);
|
||||
}
|
||||
|
||||
if (literalOptions != null) {
|
||||
literalOptions.addAll(currentOptions);
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arguments.isEmpty() && tooManyArguments != null) {
|
||||
tooManyArguments.accept(command, argumentValues);
|
||||
return null;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private <T> T useCommand(
|
||||
String command,
|
||||
List<String> arguments,
|
||||
BiConsumer<GameCommand, Arguments> tooManyArguments,
|
||||
BiConsumer<GameCommand, Arguments> unclosedQuote,
|
||||
BiFunction<GameCommand, Arguments, T> function,
|
||||
List<String> literalOptions
|
||||
) {
|
||||
command = command.substring(command.lastIndexOf(':') + 1);
|
||||
GameCommand commandBuilder = commands.get(command);
|
||||
if (commandBuilder == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Arguments args = new Arguments();
|
||||
GameCommand subCommand = processCommand(commandBuilder, new ArrayList<>(arguments), args, tooManyArguments, unclosedQuote, literalOptions);
|
||||
if (subCommand == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return function.apply(subCommand, args);
|
||||
}
|
||||
|
||||
public void execute(ICommandSender sender, String command, List<String> arguments) {
|
||||
useCommand(
|
||||
command,
|
||||
arguments,
|
||||
(cmd, args) -> {
|
||||
if (!cmd.hasPermission(sender)) {
|
||||
cmd.sendNoPermission(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
error(sender, command, arguments, args, "Incorrect argument for command");
|
||||
},
|
||||
(cmd, args) -> {
|
||||
if (!cmd.hasPermission(sender)) {
|
||||
cmd.sendNoPermission(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
error(sender, command, arguments, args, "Unclosed quoted string");
|
||||
},
|
||||
(cmd, args) -> {
|
||||
cmd.getExecutor().execute(sender, args);
|
||||
return null;
|
||||
},
|
||||
null);
|
||||
}
|
||||
|
||||
private void error(ICommandSender sender, String command, List<String> arguments, Arguments args, String title) {
|
||||
// Mimic brigadier behaviour (en-US)
|
||||
StringBuilder builder = new StringBuilder(command);
|
||||
for (Object value : args.getValues().values()) {
|
||||
builder.append(" ").append(value);
|
||||
}
|
||||
int length = builder.length();
|
||||
String pre = length >= 10 ? "..." + builder.substring(length - 9, length) : builder.toString();
|
||||
|
||||
sender.sendMessage(
|
||||
Component.text()
|
||||
.append(Component.text(title, NamedTextColor.RED))
|
||||
.append(Component.newline())
|
||||
.append(Component.text(pre + " ", NamedTextColor.GRAY))
|
||||
.append(Component.text(String.join(" ", arguments), NamedTextColor.RED, TextDecoration.UNDERLINED))
|
||||
.append(Component.text("<--[HERE]", NamedTextColor.RED, TextDecoration.ITALIC))
|
||||
);
|
||||
}
|
||||
|
||||
public List<String> suggest(ICommandSender sender, String command, List<String> arguments) {
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
return useCommand(
|
||||
command,
|
||||
arguments,
|
||||
null,
|
||||
null,
|
||||
(cmd, args) -> {
|
||||
suggestions.addAll(
|
||||
cmd.getSuggester().suggestValues(sender, args, String.valueOf(args.get(cmd.getLabel(), Object.class)))
|
||||
.stream()
|
||||
.map(value -> value.substring(value.lastIndexOf(' ') + 1))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
return suggestions;
|
||||
},
|
||||
suggestions
|
||||
);
|
||||
}
|
||||
|
||||
private static class Arguments implements GameCommandArguments {
|
||||
|
||||
private final Map<String, Object> values = new LinkedHashMap<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T get(String label, Class<T> type) {
|
||||
return (T) values.get(label);
|
||||
}
|
||||
|
||||
public void put(String label, Object value) {
|
||||
values.put(label, value);
|
||||
}
|
||||
|
||||
public Map<String, Object> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Arguments{" +
|
||||
"values=" + values +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.common.command.game.handler;
|
||||
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
|
||||
public interface ICommandHandler {
|
||||
|
||||
void registerCommand(GameCommand command);
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.common.command.game.handler.util;
|
||||
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandSuggester;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.arguments.*;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Helper class to convert DiscordSRV's abstract command tree into a brigadier one.
|
||||
*/
|
||||
public final class BrigadierUtil {
|
||||
|
||||
private static final Map<GameCommand, CommandNode<?>> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private BrigadierUtil() {}
|
||||
|
||||
public static <S> LiteralCommandNode<S> convertToBrigadier(GameCommand command, Function<S, ICommandSender> commandSenderMapper) {
|
||||
return (LiteralCommandNode<S>) convert(command, commandSenderMapper);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <S> CommandNode<S> convert(GameCommand commandBuilder, Function<S, ICommandSender> commandSenderMapper) {
|
||||
CommandNode<S> alreadyConverted = (CommandNode<S>) CACHE.get(commandBuilder);
|
||||
if (alreadyConverted != null) {
|
||||
return alreadyConverted;
|
||||
}
|
||||
|
||||
GameCommand.ArgumentType type = commandBuilder.getArgumentType();
|
||||
String label = commandBuilder.getLabel();
|
||||
GameCommandExecutor executor = commandBuilder.getExecutor();
|
||||
GameCommandSuggester suggester = commandBuilder.getSuggester();
|
||||
GameCommand redirection = commandBuilder.getRedirection();
|
||||
String requiredPermission = commandBuilder.getRequiredPermission();
|
||||
|
||||
ArgumentBuilder<S, ?> argumentBuilder;
|
||||
if (type == GameCommand.ArgumentType.LITERAL) {
|
||||
argumentBuilder = LiteralArgumentBuilder.literal(label);
|
||||
} else {
|
||||
argumentBuilder = RequiredArgumentBuilder.argument(label, convertType(commandBuilder));
|
||||
}
|
||||
|
||||
for (GameCommand child : commandBuilder.getChildren()) {
|
||||
argumentBuilder.then(convert(child, commandSenderMapper));
|
||||
}
|
||||
if (redirection != null) {
|
||||
CommandNode<S> redirectNode = (CommandNode<S>) CACHE.get(redirection);
|
||||
if (redirectNode == null) {
|
||||
redirectNode = convert(redirection, commandSenderMapper);
|
||||
}
|
||||
argumentBuilder.redirect(redirectNode);
|
||||
}
|
||||
|
||||
if (requiredPermission != null) {
|
||||
argumentBuilder.requires(sender -> {
|
||||
ICommandSender commandSender = commandSenderMapper.apply(sender);
|
||||
return commandSender.hasPermission(requiredPermission);
|
||||
});
|
||||
}
|
||||
if (executor != null) {
|
||||
argumentBuilder.executes(context -> {
|
||||
executor.execute(
|
||||
commandSenderMapper.apply(context.getSource()),
|
||||
context::getArgument
|
||||
);
|
||||
return Command.SINGLE_SUCCESS;
|
||||
});
|
||||
}
|
||||
if (suggester != null && argumentBuilder instanceof RequiredArgumentBuilder) {
|
||||
((RequiredArgumentBuilder<S, ?>) argumentBuilder).suggests((context, builder) -> {
|
||||
try {
|
||||
List<?> suggestions = suggester.suggestValues(
|
||||
commandSenderMapper.apply(context.getSource()),
|
||||
context::getArgument,
|
||||
builder.getRemaining()
|
||||
);
|
||||
suggestions.forEach(suggestion -> builder.suggest(suggestion.toString()));
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
return CompletableFuture.completedFuture(builder.build());
|
||||
});
|
||||
}
|
||||
|
||||
CommandNode<S> node = argumentBuilder.build();
|
||||
CACHE.put(commandBuilder, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
private static ArgumentType<?> convertType(GameCommand builder) {
|
||||
GameCommand.ArgumentType argumentType = builder.getArgumentType();
|
||||
double min = builder.getMinValue();
|
||||
double max = builder.getMaxValue();
|
||||
switch (argumentType) {
|
||||
case LONG: return LongArgumentType.longArg((long) min, (long) max);
|
||||
case FLOAT: return FloatArgumentType.floatArg((float) min, (float) max);
|
||||
case DOUBLE: return DoubleArgumentType.doubleArg(min, max);
|
||||
case STRING: return StringArgumentType.string();
|
||||
case STRING_WORD: return StringArgumentType.word();
|
||||
case STRING_GREEDY: return StringArgumentType.greedyString();
|
||||
case BOOLEAN: return BoolArgumentType.bool();
|
||||
case INTEGER: return IntegerArgumentType.integer((int) min, (int) max);
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.common.config.main;
|
||||
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Comment;
|
||||
|
||||
@ConfigSerializable
|
||||
public class CommandConfig {
|
||||
|
||||
@Comment("If the /discord command should be set by DiscordSRV")
|
||||
public boolean useDiscordCommand = true;
|
||||
|
||||
@Comment("Use /link as a alias for /discord link")
|
||||
public boolean useLinkAlias = false;
|
||||
|
||||
@Comment("Discord command format, player placeholders may be used")
|
||||
public String discordFormat = "[click:open_url:%discord_invite%]&b&lClick here &r&ato join our Discord server!";
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.common.config.main;
|
||||
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Comment;
|
||||
|
||||
@ConfigSerializable
|
||||
public class DiscordInviteConfig {
|
||||
|
||||
@Comment("Manually enter a invite url here, if this isn't set this is ignored and the options below will take effect")
|
||||
public String inviteUrl = "";
|
||||
|
||||
@Comment("If the bot is only in one Discord server, it will attempt to get it's vanity url")
|
||||
public boolean attemptToUseVanityUrl = true;
|
||||
|
||||
@Comment("If the bot is only in one Discord server, it will attempt to automatically create a invite for it.\n"
|
||||
+ "The bot will only attempt to do so if it has permission to \"Create Invite\"\n"
|
||||
+ "The server must also have a rules channel (available for community servers) or default channel (automatically determined by Discord)")
|
||||
public boolean autoCreateInvite = true;
|
||||
|
||||
}
|
@ -49,4 +49,10 @@ public class MainConfig implements Config {
|
||||
|
||||
@Comment("Configuration options for group-role synchronization")
|
||||
public GroupSyncConfig groupSync = new GroupSyncConfig();
|
||||
|
||||
@Comment("Command configuration")
|
||||
public CommandConfig command = new CommandConfig();
|
||||
|
||||
@Comment("The %discord_invite% placeholder configuration. The below options will be attempted in the order they are in")
|
||||
public DiscordInviteConfig invite = new DiscordInviteConfig();
|
||||
}
|
||||
|
@ -33,5 +33,5 @@ public class MirroringConfig {
|
||||
@Comment("The format of the username of mirrored messages\n"
|
||||
+ "It's recommended to include some special character if in-game messages use webhooks,\n"
|
||||
+ "in order to prevent Discord users and in-game players being grouped together")
|
||||
public String usernameFormat = "%user_effective_name% [M]";
|
||||
public String usernameFormat = "%user_effective_name% \uD83D\uDD03";
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
package com.discordsrv.common.dependency;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.scheduler.threadfactory.CountingForkJoinWorkerThreadFactory;
|
||||
@ -91,7 +92,7 @@ public class InitialDependencyLoader {
|
||||
if (discordSRV == null) {
|
||||
return;
|
||||
}
|
||||
discordSRV.invokeReload();
|
||||
discordSRV.invokeReload(DiscordSRVApi.ReloadFlag.DEFAULT_FLAGS, false);
|
||||
}
|
||||
|
||||
public void disable(DiscordSRV discordSRV) {
|
||||
|
@ -69,7 +69,11 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
||||
@Override
|
||||
public void reload() {
|
||||
synchronized (pairs) {
|
||||
pairs.values().forEach(future -> future.cancel(false));
|
||||
pairs.values().forEach(future -> {
|
||||
if (future != null) {
|
||||
future.cancel(false);
|
||||
}
|
||||
});
|
||||
pairs.clear();
|
||||
groupsToPairs.clear();
|
||||
rolesToPairs.clear();
|
||||
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.common.invite;
|
||||
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.main.DiscordInviteConfig;
|
||||
import com.discordsrv.common.module.type.AbstractModule;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DiscordInviteModule extends AbstractModule<DiscordSRV> {
|
||||
|
||||
private static final String UNKNOWN_INVITE = "https://discord.gg/#Could_not_get_invite,_please_check_your_configuration";
|
||||
private String invite;
|
||||
|
||||
public DiscordInviteModule(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
discordSRV.placeholderService().addGlobalContext(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
JDA jda = discordSRV.jda().orElse(null);
|
||||
if (jda == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DiscordInviteConfig config = discordSRV.config().invite;
|
||||
|
||||
// Manual
|
||||
String invite = config.inviteUrl;
|
||||
if (invite != null && !invite.isEmpty()) {
|
||||
this.invite = invite;
|
||||
return;
|
||||
}
|
||||
|
||||
List<Guild> guilds = jda.getGuilds();
|
||||
if (guilds.size() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Guild guild = guilds.get(0);
|
||||
|
||||
// Vanity url
|
||||
if (config.attemptToUseVanityUrl) {
|
||||
String vanityUrl = guild.getVanityUrl();
|
||||
if (vanityUrl != null) {
|
||||
this.invite = vanityUrl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Auto create
|
||||
if (config.autoCreateInvite) {
|
||||
Member selfMember = guild.getSelfMember();
|
||||
if (!selfMember.hasPermission(Permission.CREATE_INSTANT_INVITE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TextChannel channel = guild.getRulesChannel();
|
||||
if (channel == null) {
|
||||
channel = guild.getDefaultChannel();
|
||||
}
|
||||
if (channel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
channel.createInvite().setMaxAge(0).setUnique(true).queue(inv -> this.invite = inv.getUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@Placeholder("discord_invite")
|
||||
public String getInvite() {
|
||||
return invite != null ? invite : UNKNOWN_INVITE;
|
||||
}
|
||||
}
|
@ -31,7 +31,6 @@ public class StorageLinker extends CachedLinkProvider implements LinkProvider, L
|
||||
|
||||
public StorageLinker(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
discordSRV.logger().info("Using storage for linked accounts");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,13 +20,16 @@ package com.discordsrv.common.module;
|
||||
|
||||
import com.discordsrv.api.event.bus.EventPriority;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVShuttingDownEvent;
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.debug.DebugGenerateEvent;
|
||||
import com.discordsrv.common.debug.file.TextDebugFile;
|
||||
import com.discordsrv.common.function.CheckedFunction;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
import com.discordsrv.common.module.type.AbstractModule;
|
||||
import com.discordsrv.common.module.type.ModuleDelegate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -39,13 +42,20 @@ public class ModuleManager {
|
||||
|
||||
private final Set<Module> modules = new CopyOnWriteArraySet<>();
|
||||
private final Map<String, Module> moduleLookupTable = new ConcurrentHashMap<>();
|
||||
private final Map<Module, AbstractModule<?>> delegates = new ConcurrentHashMap<>();
|
||||
private final DiscordSRV discordSRV;
|
||||
private final Logger logger;
|
||||
|
||||
public ModuleManager(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.logger = new NamedLogger(discordSRV, "MODULE_MANAGER");
|
||||
discordSRV.eventBus().subscribe(this);
|
||||
}
|
||||
|
||||
public Logger logger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Module> T getModule(Class<T> moduleType) {
|
||||
return (T) moduleLookupTable.computeIfAbsent(moduleType.getName(), key -> {
|
||||
@ -62,37 +72,66 @@ public class ModuleManager {
|
||||
});
|
||||
}
|
||||
|
||||
public void register(AbstractModule<?> module) {
|
||||
private AbstractModule<?> getAbstract(Module module) {
|
||||
return module instanceof AbstractModule
|
||||
? (AbstractModule<?>) module
|
||||
: delegates.computeIfAbsent(module, mod -> new ModuleDelegate(discordSRV, mod));
|
||||
}
|
||||
|
||||
public <DT extends DiscordSRV> void registerModule(DT discordSRV, CheckedFunction<DT, AbstractModule<?>> function) {
|
||||
try {
|
||||
register(function.apply(discordSRV));
|
||||
} catch (Throwable t) {
|
||||
logger.debug("Module initialization failed", t);
|
||||
}
|
||||
}
|
||||
|
||||
public void register(Module module) {
|
||||
if (module instanceof ModuleDelegate) {
|
||||
throw new IllegalArgumentException("Cannot register a delegate");
|
||||
}
|
||||
|
||||
AbstractModule<?> abstractModule = getAbstract(module);
|
||||
|
||||
logger.debug(module + " registered");
|
||||
this.modules.add(module);
|
||||
this.moduleLookupTable.put(module.getClass().getName(), module);
|
||||
|
||||
enable(module, true);
|
||||
if (discordSRV.config() != null) {
|
||||
// Check if config is ready, if it is already we'll enable the module
|
||||
enable(abstractModule);
|
||||
}
|
||||
}
|
||||
|
||||
private void enable(Module module, boolean enableNonAbstract) {
|
||||
private void enable(AbstractModule<?> module) {
|
||||
try {
|
||||
if (module instanceof AbstractModule) {
|
||||
((AbstractModule<?>) module).enableModule();
|
||||
} else if (enableNonAbstract) {
|
||||
module.enable();
|
||||
if (module.enableModule()) {
|
||||
logger.debug(module + " enabled");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
discordSRV.logger().error("Failed to enable " + module.getClass().getSimpleName(), t);
|
||||
discordSRV.logger().error("Failed to enable " + module.toString(), t);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(Module module) {
|
||||
this.modules.remove(module);
|
||||
this.moduleLookupTable.values().removeIf(mod -> mod == module);
|
||||
if (module instanceof ModuleDelegate) {
|
||||
throw new IllegalArgumentException("Cannot unregister a delegate");
|
||||
}
|
||||
|
||||
disable(module);
|
||||
|
||||
this.modules.remove(module);
|
||||
this.moduleLookupTable.values().removeIf(mod -> mod == module);
|
||||
this.delegates.remove(module);
|
||||
}
|
||||
|
||||
private void disable(Module module) {
|
||||
AbstractModule<?> abstractModule = getAbstract(module);
|
||||
try {
|
||||
module.disable();
|
||||
logger.debug(module + " disabling");
|
||||
abstractModule.disable();
|
||||
} catch (Throwable t) {
|
||||
discordSRV.logger().error("Failed to disable " + module.getClass().getSimpleName(), t);
|
||||
discordSRV.logger().error("Failed to disable " + abstractModule.toString(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,14 +142,15 @@ public class ModuleManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(priority = EventPriority.EARLY)
|
||||
public void onReload(DiscordSRVReloadEvent event) {
|
||||
public void reload() {
|
||||
for (Module module : modules) {
|
||||
AbstractModule<?> abstractModule = getAbstract(module);
|
||||
|
||||
// Check if the module needs to be enabled due to reload
|
||||
enable(module, false);
|
||||
enable(abstractModule);
|
||||
|
||||
try {
|
||||
module.reload();
|
||||
abstractModule.reload();
|
||||
} catch (Throwable t) {
|
||||
discordSRV.logger().error("Failed to reload " + module.getClass().getSimpleName(), t);
|
||||
}
|
||||
@ -124,11 +164,13 @@ public class ModuleManager {
|
||||
builder.append("Enabled modules:");
|
||||
List<Module> disabled = new ArrayList<>();
|
||||
for (Module module : modules) {
|
||||
if (!module.isEnabled()) {
|
||||
disabled.add(module);
|
||||
AbstractModule<?> abstractModule = getAbstract(module);
|
||||
|
||||
if (!abstractModule.isEnabled()) {
|
||||
disabled.add(abstractModule);
|
||||
continue;
|
||||
}
|
||||
appendModule(builder, module);
|
||||
appendModule(builder, abstractModule);
|
||||
}
|
||||
|
||||
builder.append("\n\nDisabled modules:");
|
||||
|
@ -44,9 +44,9 @@ public abstract class AbstractModule<DT extends DiscordSRV> implements Module {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public final void enableModule() {
|
||||
public final boolean enableModule() {
|
||||
if (hasBeenEnabled || !isEnabled()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
hasBeenEnabled = true;
|
||||
@ -56,6 +56,12 @@ public abstract class AbstractModule<DT extends DiscordSRV> implements Module {
|
||||
discordSRV.eventBus().subscribe(this);
|
||||
// Ignore not having listener methods exception
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + "{enabled=" + isEnabled() + "}";
|
||||
}
|
||||
|
||||
// Utility
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.common.module.type;
|
||||
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
|
||||
public class ModuleDelegate extends AbstractModule<DiscordSRV> {
|
||||
|
||||
private final Module module;
|
||||
|
||||
public ModuleDelegate(DiscordSRV discordSRV, Module module) {
|
||||
super(discordSRV, new NamedLogger(discordSRV, module.getClass().getName()));
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return module.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
module.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
module.reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
module.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String original = super.toString();
|
||||
return original.substring(0, original.length() - 1) + ",module=" + module.getClass().getName() + "(" + module + ")}";
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
|
||||
package com.discordsrv.common;
|
||||
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.MainConfig;
|
||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
||||
@ -38,6 +39,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionConfig> {
|
||||
|
||||
public static final MockDiscordSRV INSTANCE = new MockDiscordSRV();
|
||||
@ -102,6 +104,11 @@ public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionCon
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICommandHandler commandHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull AbstractPlayerProvider<?, ?> playerProvider() {
|
||||
return null;
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.discordsrv.config;
|
||||
|
||||
import com.discordsrv.common.AbstractDiscordSRV;
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.MainConfig;
|
||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
||||
@ -35,6 +36,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionConfig> {
|
||||
|
||||
@Override
|
||||
@ -77,6 +79,11 @@ public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionCon
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICommandHandler commandHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull AbstractPlayerProvider<?, ?> playerProvider() {
|
||||
return null;
|
||||
|
@ -1,5 +1,11 @@
|
||||
apply from: rootProject.file('buildscript/runtime.gradle')
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = 'sponge.jarinjar'
|
||||
|
||||
// Relocations in buildscript/relocations.gradle
|
||||
}
|
||||
|
||||
var spongeVersion = '8.0.0'
|
||||
allprojects {
|
||||
repositories {
|
||||
@ -31,9 +37,3 @@ dependencies {
|
||||
implementation 'dev.vankka:dependencydownload-jarinjar-bootstrap:' + rootProject.ddVersion
|
||||
compileOnlyApi project(':sponge:sponge-loader')
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = 'sponge.jarinjar'
|
||||
|
||||
// Relocations in buildscript/relocations.gradle
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import com.discordsrv.common.dependency.InitialDependencyLoader;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
||||
import com.discordsrv.sponge.bootstrap.ISpongeBootstrap;
|
||||
import com.discordsrv.sponge.command.game.handler.SpongeCommandHandler;
|
||||
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
|
||||
import dev.vankka.mcdependencydownload.bootstrap.AbstractBootstrap;
|
||||
import dev.vankka.mcdependencydownload.bootstrap.classpath.JarInJarClasspathAppender;
|
||||
@ -39,6 +40,7 @@ public class DiscordSRVSpongeBootstrap extends AbstractBootstrap implements ISpo
|
||||
private final ClasspathAppender classpathAppender;
|
||||
private final InitialDependencyLoader dependencies;
|
||||
private SpongeDiscordSRV discordSRV;
|
||||
private SpongeCommandHandler commandHandler;
|
||||
|
||||
private final PluginContainer pluginContainer;
|
||||
private final Game game;
|
||||
@ -63,11 +65,14 @@ public class DiscordSRVSpongeBootstrap extends AbstractBootstrap implements ISpo
|
||||
@Override
|
||||
public void onConstruct() {
|
||||
dependencies.load();
|
||||
|
||||
this.commandHandler = new SpongeCommandHandler(() -> discordSRV, pluginContainer);
|
||||
game.eventManager().registerListeners(pluginContainer, commandHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStarted() {
|
||||
dependencies.enable(() -> this.discordSRV = new SpongeDiscordSRV(logger, classpathAppender, dataDirectory, pluginContainer, game));
|
||||
dependencies.enable(() -> this.discordSRV = new SpongeDiscordSRV(logger, classpathAppender, dataDirectory, pluginContainer, game, commandHandler));
|
||||
if (discordSRV != null) {
|
||||
discordSRV.invokeServerStarted();
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.discordsrv.sponge;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.MainConfig;
|
||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
||||
@ -27,6 +28,7 @@ import com.discordsrv.common.debug.data.OnlineMode;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.plugin.PluginManager;
|
||||
import com.discordsrv.common.server.ServerDiscordSRV;
|
||||
import com.discordsrv.sponge.command.game.handler.SpongeCommandHandler;
|
||||
import com.discordsrv.sponge.console.SpongeConsole;
|
||||
import com.discordsrv.sponge.player.SpongePlayerProvider;
|
||||
import com.discordsrv.sponge.plugin.SpongePluginManager;
|
||||
@ -52,8 +54,16 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
|
||||
private final SpongeConsole console;
|
||||
private final SpongePlayerProvider playerProvider;
|
||||
private final SpongePluginManager pluginManager;
|
||||
private final SpongeCommandHandler commandHandler;
|
||||
|
||||
public SpongeDiscordSRV(Logger logger, ClasspathAppender classpathAppender, Path dataDirectory, PluginContainer pluginContainer, Game game) {
|
||||
public SpongeDiscordSRV(
|
||||
Logger logger,
|
||||
ClasspathAppender classpathAppender,
|
||||
Path dataDirectory,
|
||||
PluginContainer pluginContainer,
|
||||
Game game,
|
||||
SpongeCommandHandler commandHandler
|
||||
) {
|
||||
this.logger = logger;
|
||||
this.classpathAppender = classpathAppender;
|
||||
this.dataDirectory = dataDirectory;
|
||||
@ -64,6 +74,7 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
|
||||
this.console = new SpongeConsole(this);
|
||||
this.playerProvider = new SpongePlayerProvider(this);
|
||||
this.pluginManager = new SpongePluginManager(this);
|
||||
this.commandHandler = commandHandler;
|
||||
|
||||
load();
|
||||
}
|
||||
@ -123,6 +134,11 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
|
||||
return classpathAppender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICommandHandler commandHandler() {
|
||||
return commandHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
|
||||
return null;
|
||||
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.sponge.command.game.handler;
|
||||
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import com.discordsrv.sponge.SpongeDiscordSRV;
|
||||
import com.discordsrv.sponge.command.game.sender.SpongeCommandSender;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.spongepowered.api.SystemSubject;
|
||||
import org.spongepowered.api.command.Command;
|
||||
import org.spongepowered.api.command.CommandResult;
|
||||
import org.spongepowered.api.command.parameter.CommandContext;
|
||||
import org.spongepowered.api.command.parameter.Parameter;
|
||||
import org.spongepowered.api.command.parameter.managed.Flag;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
import org.spongepowered.api.event.lifecycle.RegisterCommandEvent;
|
||||
import org.spongepowered.api.service.permission.Subject;
|
||||
import org.spongepowered.plugin.PluginContainer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Sponge has its own api for interacting with Brigadier...
|
||||
*/
|
||||
public class SpongeCommandHandler implements ICommandHandler {
|
||||
|
||||
private final Map<String, Command.Parameterized> commands = new LinkedHashMap<>();
|
||||
private final Supplier<SpongeDiscordSRV> discordSRV;
|
||||
private final PluginContainer container;
|
||||
|
||||
public SpongeCommandHandler(Supplier<SpongeDiscordSRV> discordSRV, PluginContainer container) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCommand(GameCommand command) {
|
||||
commands.put(command.getLabel(), remap(command));
|
||||
}
|
||||
|
||||
private ICommandSender getSender(Subject subject, Audience audience) {
|
||||
SpongeDiscordSRV discordSRV = this.discordSRV.get();
|
||||
if (discordSRV != null) {
|
||||
if (subject instanceof ServerPlayer) {
|
||||
return discordSRV.playerProvider().player((ServerPlayer) subject);
|
||||
} else if (subject instanceof SystemSubject) {
|
||||
return discordSRV.console();
|
||||
}
|
||||
}
|
||||
|
||||
return new SpongeCommandSender(discordSRV, () -> subject, () -> audience);
|
||||
}
|
||||
|
||||
private ICommandSender getSender(CommandContext context) {
|
||||
return getSender(context.subject(), context.cause().audience());
|
||||
}
|
||||
|
||||
private Command.Parameterized remap(GameCommand command) {
|
||||
GameCommand redirection = command.getRedirection();
|
||||
if (redirection != null) {
|
||||
command = redirection;
|
||||
}
|
||||
|
||||
//GameCommandSuggester suggester = command.getSuggester();
|
||||
GameCommandExecutor executor = command.getExecutor();
|
||||
String permission = command.getRequiredPermission();
|
||||
|
||||
Command.Builder builder = Command.builder();
|
||||
builder.addFlag(Flag.builder().alias(command.getLabel()).build());
|
||||
|
||||
if (permission != null) {
|
||||
builder.permission(permission);
|
||||
}
|
||||
for (GameCommand child : command.getChildren()) {
|
||||
builder.addChild(remap(child));
|
||||
}
|
||||
|
||||
if (executor != null) {
|
||||
builder.executor(context -> {
|
||||
executor.execute(getSender(context), new GameCommandArguments() {
|
||||
@Override
|
||||
public <T> T get(String label, Class<T> type) {
|
||||
return context.one(new Parameter.Key<T>() {
|
||||
@Override
|
||||
public String key() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstance(Object value) {
|
||||
return type.isInstance(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T cast(Object value) {
|
||||
return (T) value;
|
||||
}
|
||||
}).orElse(null);
|
||||
}
|
||||
});
|
||||
|
||||
return CommandResult.success();
|
||||
});
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onRegister(RegisterCommandEvent<Command.Parameterized> event) {
|
||||
for (Map.Entry<String, Command.Parameterized> entry : commands.entrySet()) {
|
||||
event.register(container, entry.getValue(), entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.sponge.command.game.sender;
|
||||
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import com.discordsrv.sponge.SpongeDiscordSRV;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.api.command.exception.CommandException;
|
||||
import org.spongepowered.api.service.permission.Subject;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SpongeCommandSender implements ICommandSender {
|
||||
|
||||
protected final SpongeDiscordSRV discordSRV;
|
||||
protected final Supplier<Subject> subjectSupplier;
|
||||
protected final Supplier<Audience> audienceSupplier;
|
||||
|
||||
public SpongeCommandSender(SpongeDiscordSRV discordSRV, Supplier<Subject> subjectSupplier, Supplier<Audience> audienceSupplier) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.subjectSupplier = subjectSupplier;
|
||||
this.audienceSupplier = audienceSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return subjectSupplier.get().hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
try {
|
||||
Subject subject = subjectSupplier.get();
|
||||
discordSRV.game().server().commandManager().process((Subject & Audience) subject, command);
|
||||
} catch (CommandException ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return audienceSupplier.get();
|
||||
}
|
||||
}
|
@ -22,40 +22,19 @@ import com.discordsrv.common.console.Console;
|
||||
import com.discordsrv.common.logging.backend.LoggingBackend;
|
||||
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
||||
import com.discordsrv.sponge.SpongeDiscordSRV;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.api.command.exception.CommandException;
|
||||
import com.discordsrv.sponge.command.game.sender.SpongeCommandSender;
|
||||
|
||||
public class SpongeConsole implements Console {
|
||||
public class SpongeConsole extends SpongeCommandSender implements Console {
|
||||
|
||||
private final SpongeDiscordSRV discordSRV;
|
||||
private final LoggingBackend loggingBackend;
|
||||
|
||||
public SpongeConsole(SpongeDiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
super(discordSRV, () -> discordSRV.game().systemSubject(), () -> discordSRV.game().systemSubject());
|
||||
this.loggingBackend = Log4JLoggerImpl.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return discordSRV.game().systemSubject().hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
try {
|
||||
discordSRV.game().server().commandManager().process(
|
||||
discordSRV.game().systemSubject(), command);
|
||||
} catch (CommandException ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggingBackend loggingBackend() {
|
||||
return loggingBackend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return discordSRV.game().systemSubject();
|
||||
}
|
||||
}
|
||||
|
@ -21,31 +21,21 @@ package com.discordsrv.sponge.player;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.player.IPlayer;
|
||||
import com.discordsrv.sponge.SpongeDiscordSRV;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import com.discordsrv.sponge.command.game.sender.SpongeCommandSender;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.api.command.exception.CommandException;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
|
||||
public class SpongePlayer extends SpongeOfflinePlayer implements IPlayer {
|
||||
public class SpongePlayer extends SpongeCommandSender implements IPlayer {
|
||||
|
||||
private final ServerPlayer player;
|
||||
private final Identity identity;
|
||||
|
||||
public SpongePlayer(SpongeDiscordSRV discordSRV, ServerPlayer player) {
|
||||
super(discordSRV, player.user());
|
||||
super(discordSRV, () -> player, () -> player);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
try {
|
||||
discordSRV.game().server().commandManager().process(player, command);
|
||||
} catch (CommandException ignored) {}
|
||||
this.identity = Identity.identity(player.uniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -53,13 +43,18 @@ public class SpongePlayer extends SpongeOfflinePlayer implements IPlayer {
|
||||
return discordSRV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String username() {
|
||||
return player.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Component displayName() {
|
||||
return player.displayName().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return player;
|
||||
public @NotNull Identity identity() {
|
||||
return identity;
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ package com.discordsrv.sponge.player;
|
||||
import com.discordsrv.common.player.IOfflinePlayer;
|
||||
import com.discordsrv.common.server.player.ServerPlayerProvider;
|
||||
import com.discordsrv.sponge.SpongeDiscordSRV;
|
||||
import org.spongepowered.api.entity.living.player.Player;
|
||||
import org.spongepowered.api.entity.living.player.User;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
@ -64,7 +63,7 @@ public class SpongePlayerProvider extends ServerPlayerProvider<SpongePlayer, Spo
|
||||
removePlayer(event.player().uniqueId());
|
||||
}
|
||||
|
||||
public SpongePlayer player(Player player) {
|
||||
public SpongePlayer player(ServerPlayer player) {
|
||||
return player(player.uniqueId()).orElseThrow(() -> new IllegalStateException("Player not available"));
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,12 @@
|
||||
apply from: rootProject.file('buildscript/standalone.gradle')
|
||||
apply plugin: 'net.kyori.blossom'
|
||||
|
||||
shadowJar {
|
||||
archiveBaseName = 'DiscordSRV-Velocity'
|
||||
|
||||
// Relocations in buildscript/relocations.gradle
|
||||
}
|
||||
|
||||
repositories {
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
@ -33,12 +39,6 @@ jar {
|
||||
dependsOn blossomSourceReplacementJava
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveBaseName = 'DiscordSRV-Velocity'
|
||||
|
||||
// Relocations in buildscript/relocations.gradle
|
||||
}
|
||||
|
||||
blossom {
|
||||
var mainClass = 'src/main/java/com/discordsrv/velocity/DiscordSRVVelocityBootstrap.java'
|
||||
replaceToken('@VERSION@', project.version, mainClass)
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
package com.discordsrv.velocity;
|
||||
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.MainConfig;
|
||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
||||
@ -27,6 +28,7 @@ import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.plugin.PluginManager;
|
||||
import com.discordsrv.common.scheduler.StandardScheduler;
|
||||
import com.discordsrv.proxy.ProxyDiscordSRV;
|
||||
import com.discordsrv.velocity.command.game.handler.VelocityCommandHandler;
|
||||
import com.discordsrv.velocity.console.VelocityConsole;
|
||||
import com.discordsrv.velocity.player.VelocityPlayerProvider;
|
||||
import com.discordsrv.velocity.plugin.VelocityPluginManager;
|
||||
@ -50,6 +52,7 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
|
||||
private final VelocityConsole console;
|
||||
private final VelocityPlayerProvider playerProvider;
|
||||
private final VelocityPluginManager pluginManager;
|
||||
private final VelocityCommandHandler commandHandler;
|
||||
|
||||
public VelocityDiscordSRV(Object plugin, Logger logger, ClasspathAppender classpathAppender, ProxyServer proxyServer, PluginContainer pluginContainer, Path dataDirectory) {
|
||||
this.plugin = plugin;
|
||||
@ -63,6 +66,7 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
|
||||
this.console = new VelocityConsole(this);
|
||||
this.playerProvider = new VelocityPlayerProvider(this);
|
||||
this.pluginManager = new VelocityPluginManager(this);
|
||||
this.commandHandler = new VelocityCommandHandler(this);
|
||||
|
||||
load();
|
||||
}
|
||||
@ -124,6 +128,11 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
|
||||
return classpathAppender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICommandHandler commandHandler() {
|
||||
return commandHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
|
||||
return null;
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.velocity.command.game.handler;
|
||||
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||
import com.discordsrv.common.command.game.handler.util.BrigadierUtil;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import com.discordsrv.velocity.VelocityDiscordSRV;
|
||||
import com.discordsrv.velocity.command.game.sender.VelocityCommandSender;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import com.velocitypowered.api.command.BrigadierCommand;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
|
||||
public class VelocityCommandHandler implements ICommandHandler {
|
||||
|
||||
private final VelocityDiscordSRV discordSRV;
|
||||
|
||||
public VelocityCommandHandler(VelocityDiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
private ICommandSender getSender(CommandSource source) {
|
||||
if (source instanceof Player) {
|
||||
return discordSRV.playerProvider().player((Player) source);
|
||||
} else if (source instanceof ConsoleCommandSource) {
|
||||
return discordSRV.console();
|
||||
} else {
|
||||
return new VelocityCommandSender(discordSRV, source);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCommand(GameCommand command) {
|
||||
LiteralCommandNode<CommandSource> node = BrigadierUtil.convertToBrigadier(command, this::getSender);
|
||||
discordSRV.proxy().getCommandManager().register(new BrigadierCommand(node));
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.velocity.command.game.sender;
|
||||
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import com.discordsrv.velocity.VelocityDiscordSRV;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class VelocityCommandSender implements ICommandSender {
|
||||
|
||||
protected final VelocityDiscordSRV discordSRV;
|
||||
protected final CommandSource commandSource;
|
||||
|
||||
public VelocityCommandSender(VelocityDiscordSRV discordSRV, CommandSource commandSource) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.commandSource = commandSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return commandSource.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
discordSRV.proxy().getCommandManager().executeAsync(commandSource, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return commandSource;
|
||||
}
|
||||
}
|
@ -22,37 +22,19 @@ import com.discordsrv.common.console.Console;
|
||||
import com.discordsrv.common.logging.backend.LoggingBackend;
|
||||
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
||||
import com.discordsrv.velocity.VelocityDiscordSRV;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import com.discordsrv.velocity.command.game.sender.VelocityCommandSender;
|
||||
|
||||
public class VelocityConsole implements Console {
|
||||
public class VelocityConsole extends VelocityCommandSender implements Console {
|
||||
|
||||
private final VelocityDiscordSRV discordSRV;
|
||||
private final LoggingBackend loggingBackend;
|
||||
|
||||
public VelocityConsole(VelocityDiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
super(discordSRV, discordSRV.proxy().getConsoleCommandSource());
|
||||
this.loggingBackend = Log4JLoggerImpl.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return discordSRV.proxy().getConsoleCommandSource().hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
discordSRV.proxy().getCommandManager().executeAsync(
|
||||
discordSRV.proxy().getConsoleCommandSource(), command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggingBackend loggingBackend() {
|
||||
return loggingBackend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return discordSRV.proxy().getConsoleCommandSource();
|
||||
}
|
||||
}
|
||||
|
@ -21,32 +21,21 @@ package com.discordsrv.velocity.player;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.player.IPlayer;
|
||||
import com.discordsrv.velocity.VelocityDiscordSRV;
|
||||
import com.discordsrv.velocity.command.game.sender.VelocityCommandSender;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class VelocityPlayer implements IPlayer {
|
||||
public class VelocityPlayer extends VelocityCommandSender implements IPlayer {
|
||||
|
||||
private final VelocityDiscordSRV discordSRV;
|
||||
private final Player player;
|
||||
|
||||
public VelocityPlayer(VelocityDiscordSRV discordSRV, Player player) {
|
||||
this.discordSRV = discordSRV;
|
||||
super(discordSRV, player);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
discordSRV.proxy().getCommandManager().executeAsync(player, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscordSRV discordSRV() {
|
||||
return discordSRV;
|
||||
@ -70,9 +59,4 @@ public class VelocityPlayer implements IPlayer {
|
||||
() -> Component.text(player.getUsername())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Audience audience() {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user