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.ApiStatus;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The DiscordSRV API.
|
* 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;
|
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;
|
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 {
|
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 {
|
plugins {
|
||||||
id 'com.github.johnrengelman.shadow' version '7.1.1' apply false
|
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 'net.kyori.blossom' version '1.2.0' apply false
|
||||||
id 'dev.vankka.dependencydownload.plugin' version '1.1.5-SNAPSHOT' apply false
|
id 'dev.vankka.dependencydownload.plugin' version '1.1.5-SNAPSHOT' apply false
|
||||||
}
|
}
|
||||||
@ -17,8 +17,8 @@ ext {
|
|||||||
// Configurate
|
// Configurate
|
||||||
configurateVersion = '4.1.2'
|
configurateVersion = '4.1.2'
|
||||||
// Adventure & Adventure Platform
|
// Adventure & Adventure Platform
|
||||||
adventureVersion = '4.9.1'
|
adventureVersion = '4.10.0'
|
||||||
adventurePlatformVersion = '4.0.0'
|
adventurePlatformVersion = '4.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@ -59,6 +59,12 @@ allprojects {
|
|||||||
includeGroup 'me.scarsz'
|
includeGroup 'me.scarsz'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
url 'https://libraries.minecraft.net'
|
||||||
|
content {
|
||||||
|
includeGroup 'com.mojang'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get dependencies from central last, everything else should be filtered
|
// Get dependencies from central last, everything else should be filtered
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -1,5 +1,30 @@
|
|||||||
|
import dev.vankka.dependencydownload.task.GenerateDependencyDownloadResourceTask
|
||||||
|
|
||||||
apply from: rootProject.file('buildscript/runtime.gradle')
|
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 {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
exclusiveContent {
|
exclusiveContent {
|
||||||
@ -41,13 +66,18 @@ dependencies {
|
|||||||
// Adventure
|
// Adventure
|
||||||
runtimeDownloadApi 'net.kyori:adventure-platform-bukkit:' + rootProject.adventurePlatformVersion
|
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
|
// Integrations
|
||||||
compileOnly 'net.milkbowl.vault:VaultAPI:1.7'
|
compileOnly 'net.milkbowl.vault:VaultAPI:1.7'
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
processResources {
|
||||||
archiveFileName = 'bukkit.jarinjar'
|
dependsOn(generateResourceForCommodore)
|
||||||
|
|
||||||
relocate 'net.kyori', 'com.discordsrv.dependencies.net.kyori'
|
|
||||||
// More relocations in buildscript/relocations.gradle
|
|
||||||
}
|
}
|
||||||
|
@ -5,3 +5,14 @@ description: ""
|
|||||||
authors: [Scarsz, Vankka]
|
authors: [Scarsz, Vankka]
|
||||||
load: STARTUP
|
load: STARTUP
|
||||||
api-version: 1.13
|
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;
|
package com.discordsrv.bukkit;
|
||||||
|
|
||||||
import com.discordsrv.api.DiscordSRVApi;
|
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.connection.BukkitConnectionConfig;
|
||||||
import com.discordsrv.bukkit.config.main.BukkitConfig;
|
import com.discordsrv.bukkit.config.main.BukkitConfig;
|
||||||
import com.discordsrv.bukkit.config.manager.BukkitConfigManager;
|
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.player.BukkitPlayerProvider;
|
||||||
import com.discordsrv.bukkit.plugin.BukkitPluginManager;
|
import com.discordsrv.bukkit.plugin.BukkitPluginManager;
|
||||||
import com.discordsrv.bukkit.scheduler.BukkitScheduler;
|
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.ConnectionConfigManager;
|
||||||
import com.discordsrv.common.config.manager.MainConfigManager;
|
import com.discordsrv.common.config.manager.MainConfigManager;
|
||||||
import com.discordsrv.common.debug.data.OnlineMode;
|
import com.discordsrv.common.debug.data.OnlineMode;
|
||||||
@ -60,6 +62,7 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
|
|||||||
private final BukkitConsole console;
|
private final BukkitConsole console;
|
||||||
private final BukkitPlayerProvider playerProvider;
|
private final BukkitPlayerProvider playerProvider;
|
||||||
private final BukkitPluginManager pluginManager;
|
private final BukkitPluginManager pluginManager;
|
||||||
|
private AbstractBukkitCommandHandler commandHandler;
|
||||||
|
|
||||||
private final BukkitConnectionConfigManager connectionConfigManager;
|
private final BukkitConnectionConfigManager connectionConfigManager;
|
||||||
private final BukkitConfigManager configManager;
|
private final BukkitConfigManager configManager;
|
||||||
@ -157,6 +160,11 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
|
|||||||
return bootstrap.getClasspathAppender();
|
return bootstrap.getClasspathAppender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICommandHandler commandHandler() {
|
||||||
|
return commandHandler;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionConfigManager<BukkitConnectionConfig> connectionConfigManager() {
|
public ConnectionConfigManager<BukkitConnectionConfig> connectionConfigManager() {
|
||||||
return connectionConfigManager;
|
return connectionConfigManager;
|
||||||
@ -176,15 +184,19 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
|
|||||||
this.audiences = BukkitAudiences.create(bootstrap.getPlugin());
|
this.audiences = BukkitAudiences.create(bootstrap.getPlugin());
|
||||||
server().getPluginManager().registerEvents(new BukkitConnectionListener(this), plugin());
|
server().getPluginManager().registerEvents(new BukkitConnectionListener(this), plugin());
|
||||||
|
|
||||||
super.enable();
|
// Command handler
|
||||||
|
commandHandler = AbstractBukkitCommandHandler.get(this);
|
||||||
|
|
||||||
// Register listeners
|
// Register listeners
|
||||||
server().getPluginManager().registerEvents(BukkitChatListener.get(this), plugin());
|
server().getPluginManager().registerEvents(BukkitChatListener.get(this), plugin());
|
||||||
server().getPluginManager().registerEvents(new BukkitDeathListener(this), plugin());
|
server().getPluginManager().registerEvents(new BukkitDeathListener(this), plugin());
|
||||||
server().getPluginManager().registerEvents(new BukkitStatusMessageListener(this), plugin());
|
server().getPluginManager().registerEvents(new BukkitStatusMessageListener(this), plugin());
|
||||||
|
|
||||||
|
// Modules
|
||||||
registerModule(VaultIntegration::new);
|
registerModule(VaultIntegration::new);
|
||||||
registerModule(MinecraftToDiscordChatModule::new);
|
registerModule(MinecraftToDiscordChatModule::new);
|
||||||
|
|
||||||
|
super.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ import dev.vankka.mcdependencydownload.classloader.JarInJarClassLoader;
|
|||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class DiscordSRVBukkitBootstrap extends BukkitBootstrap {
|
public class DiscordSRVBukkitBootstrap extends BukkitBootstrap {
|
||||||
|
|
||||||
@ -40,11 +42,25 @@ public class DiscordSRVBukkitBootstrap extends BukkitBootstrap {
|
|||||||
this.dependencies = new InitialDependencyLoader(
|
this.dependencies = new InitialDependencyLoader(
|
||||||
logger,
|
logger,
|
||||||
plugin.getDataFolder().toPath(),
|
plugin.getDataFolder().toPath(),
|
||||||
new String[] {"dependencies/runtimeDownload-bukkit.txt"},
|
getDependencyResources(),
|
||||||
getClasspathAppender()
|
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
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
dependencies.loadAndEnable(() -> this.discordSRV = new BukkitDiscordSRV(this, logger));
|
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;
|
package com.discordsrv.bukkit.console;
|
||||||
|
|
||||||
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||||
|
import com.discordsrv.bukkit.command.game.sender.BukkitCommandSender;
|
||||||
import com.discordsrv.common.console.Console;
|
import com.discordsrv.common.console.Console;
|
||||||
import com.discordsrv.common.logging.NamedLogger;
|
import com.discordsrv.common.logging.NamedLogger;
|
||||||
import com.discordsrv.common.logging.backend.LoggingBackend;
|
import com.discordsrv.common.logging.backend.LoggingBackend;
|
||||||
import com.discordsrv.common.logging.backend.impl.JavaLoggerImpl;
|
import com.discordsrv.common.logging.backend.impl.JavaLoggerImpl;
|
||||||
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
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;
|
private final LoggingBackend loggingBackend;
|
||||||
|
|
||||||
public BukkitConsole(BukkitDiscordSRV discordSRV) {
|
public BukkitConsole(BukkitDiscordSRV discordSRV) {
|
||||||
this.discordSRV = discordSRV;
|
super(discordSRV, discordSRV.server().getConsoleSender(), () -> discordSRV.audiences().console());
|
||||||
|
|
||||||
LoggingBackend logging;
|
LoggingBackend logging;
|
||||||
try {
|
try {
|
||||||
@ -49,25 +47,8 @@ public class BukkitConsole implements Console {
|
|||||||
this.loggingBackend = logging;
|
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
|
@Override
|
||||||
public LoggingBackend loggingBackend() {
|
public LoggingBackend loggingBackend() {
|
||||||
return loggingBackend;
|
return loggingBackend;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Audience audience() {
|
|
||||||
return discordSRV.audiences().console();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,6 @@ public class BukkitOfflinePlayer implements IOfflinePlayer {
|
|||||||
return discordSRV;
|
return discordSRV;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("NullabilityProblems")
|
|
||||||
@Override
|
@Override
|
||||||
public String username() {
|
public String username() {
|
||||||
return offlinePlayer.getName();
|
return offlinePlayer.getName();
|
||||||
|
@ -19,36 +19,25 @@
|
|||||||
package com.discordsrv.bukkit.player;
|
package com.discordsrv.bukkit.player;
|
||||||
|
|
||||||
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||||
|
import com.discordsrv.bukkit.command.game.sender.BukkitCommandSender;
|
||||||
import com.discordsrv.bukkit.component.util.PaperComponentUtil;
|
import com.discordsrv.bukkit.component.util.PaperComponentUtil;
|
||||||
import com.discordsrv.common.DiscordSRV;
|
import com.discordsrv.common.DiscordSRV;
|
||||||
import com.discordsrv.common.component.util.ComponentUtil;
|
import com.discordsrv.common.component.util.ComponentUtil;
|
||||||
import com.discordsrv.common.player.IPlayer;
|
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.kyori.adventure.text.Component;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@SuppressWarnings("NullableProblems") // BukkitOfflinePlayer nullability
|
public class BukkitPlayer extends BukkitCommandSender implements IPlayer {
|
||||||
public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer {
|
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final Audience audience;
|
private final Identity identity;
|
||||||
|
|
||||||
public BukkitPlayer(BukkitDiscordSRV discordSRV, Player player) {
|
public BukkitPlayer(BukkitDiscordSRV discordSRV, Player player) {
|
||||||
super(discordSRV, player);
|
super(discordSRV, player, () -> discordSRV.audiences().player(player));
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.audience = discordSRV.audiences().player(player);
|
this.identity = Identity.identity(player.getUniqueId());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(String permission) {
|
|
||||||
return player.hasPermission(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void runCommand(String command) {
|
|
||||||
discordSRV.scheduler().runOnMainThread(() ->
|
|
||||||
discordSRV.server().dispatchCommand(player, command));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -56,6 +45,11 @@ public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer {
|
|||||||
return discordSRV;
|
return discordSRV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String username() {
|
||||||
|
return player.getName();
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Paper
|
@SuppressWarnings("deprecation") // Paper
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Component displayName() {
|
public @NotNull Component displayName() {
|
||||||
@ -65,7 +59,7 @@ public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Audience audience() {
|
public @NotNull Identity identity() {
|
||||||
return audience;
|
return identity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -52,8 +52,8 @@ public class BukkitPlayerProvider extends ServerPlayerProvider<BukkitPlayer, Buk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerLogin(PlayerLoginEvent event) {
|
||||||
addPlayer(event.getPlayer(), false);
|
addPlayer(event.getPlayer(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
apply from: rootProject.file('buildscript/runtime.gradle')
|
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 {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
exclusiveContent {
|
exclusiveContent {
|
||||||
@ -32,10 +43,3 @@ dependencies {
|
|||||||
runtimeDownloadApi 'net.kyori:adventure-platform-bungeecord:' + rootProject.adventurePlatformVersion
|
runtimeDownloadApi 'net.kyori:adventure-platform-bungeecord:' + rootProject.adventurePlatformVersion
|
||||||
runtimeDownloadApi 'net.kyori:adventure-text-serializer-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;
|
package com.discordsrv.bungee;
|
||||||
|
|
||||||
|
import com.discordsrv.bungee.command.game.handler.BungeeCommandHandler;
|
||||||
import com.discordsrv.bungee.console.BungeeConsole;
|
import com.discordsrv.bungee.console.BungeeConsole;
|
||||||
import com.discordsrv.bungee.player.BungeePlayerProvider;
|
import com.discordsrv.bungee.player.BungeePlayerProvider;
|
||||||
import com.discordsrv.bungee.plugin.BungeePluginManager;
|
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.connection.ConnectionConfig;
|
||||||
import com.discordsrv.common.config.main.MainConfig;
|
import com.discordsrv.common.config.main.MainConfig;
|
||||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
||||||
@ -49,6 +51,7 @@ public class BungeeDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConf
|
|||||||
private final BungeeConsole console;
|
private final BungeeConsole console;
|
||||||
private final BungeePlayerProvider playerProvider;
|
private final BungeePlayerProvider playerProvider;
|
||||||
private final BungeePluginManager pluginManager;
|
private final BungeePluginManager pluginManager;
|
||||||
|
private BungeeCommandHandler commandHandler;
|
||||||
|
|
||||||
public BungeeDiscordSRV(DiscordSRVBungeeBootstrap bootstrap, Logger logger) {
|
public BungeeDiscordSRV(DiscordSRVBungeeBootstrap bootstrap, Logger logger) {
|
||||||
this.bootstrap = bootstrap;
|
this.bootstrap = bootstrap;
|
||||||
@ -120,6 +123,11 @@ public class BungeeDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConf
|
|||||||
return bootstrap.getClasspathAppender();
|
return bootstrap.getClasspathAppender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICommandHandler commandHandler() {
|
||||||
|
return commandHandler;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
|
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
|
||||||
return null;
|
return null;
|
||||||
@ -135,6 +143,8 @@ public class BungeeDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConf
|
|||||||
// Player related
|
// Player related
|
||||||
this.audiences = BungeeAudiences.create(bootstrap.getPlugin());
|
this.audiences = BungeeAudiences.create(bootstrap.getPlugin());
|
||||||
|
|
||||||
|
this.commandHandler = new BungeeCommandHandler(this);
|
||||||
|
|
||||||
super.enable();
|
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;
|
package com.discordsrv.bungee.console;
|
||||||
|
|
||||||
import com.discordsrv.bungee.BungeeDiscordSRV;
|
import com.discordsrv.bungee.BungeeDiscordSRV;
|
||||||
|
import com.discordsrv.bungee.command.game.sender.BungeeCommandSender;
|
||||||
import com.discordsrv.common.console.Console;
|
import com.discordsrv.common.console.Console;
|
||||||
import com.discordsrv.common.logging.backend.LoggingBackend;
|
import com.discordsrv.common.logging.backend.LoggingBackend;
|
||||||
import com.discordsrv.common.logging.backend.impl.JavaLoggerImpl;
|
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;
|
private final LoggingBackend loggingBackend;
|
||||||
|
|
||||||
public BungeeConsole(BungeeDiscordSRV discordSRV) {
|
public BungeeConsole(BungeeDiscordSRV discordSRV) {
|
||||||
this.discordSRV = discordSRV;
|
super(discordSRV, discordSRV.proxy().getConsole(), () -> discordSRV.audiences().console());
|
||||||
this.loggingBackend = JavaLoggerImpl.getRoot();
|
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
|
@Override
|
||||||
public LoggingBackend loggingBackend() {
|
public LoggingBackend loggingBackend() {
|
||||||
return loggingBackend;
|
return loggingBackend;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Audience audience() {
|
|
||||||
return discordSRV.audiences().console();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,37 +19,24 @@
|
|||||||
package com.discordsrv.bungee.player;
|
package com.discordsrv.bungee.player;
|
||||||
|
|
||||||
import com.discordsrv.bungee.BungeeDiscordSRV;
|
import com.discordsrv.bungee.BungeeDiscordSRV;
|
||||||
|
import com.discordsrv.bungee.command.game.sender.BungeeCommandSender;
|
||||||
import com.discordsrv.bungee.component.util.BungeeComponentUtil;
|
import com.discordsrv.bungee.component.util.BungeeComponentUtil;
|
||||||
import com.discordsrv.common.DiscordSRV;
|
import com.discordsrv.common.DiscordSRV;
|
||||||
import com.discordsrv.common.player.IPlayer;
|
import com.discordsrv.common.player.IPlayer;
|
||||||
import net.kyori.adventure.audience.Audience;
|
|
||||||
import net.kyori.adventure.identity.Identity;
|
import net.kyori.adventure.identity.Identity;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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 ProxiedPlayer player;
|
||||||
private final Identity identity;
|
private final Identity identity;
|
||||||
private final Audience audience;
|
|
||||||
|
|
||||||
public BungeePlayer(BungeeDiscordSRV discordSRV, ProxiedPlayer player) {
|
public BungeePlayer(BungeeDiscordSRV discordSRV, ProxiedPlayer player) {
|
||||||
this.discordSRV = discordSRV;
|
super(discordSRV, player, () -> discordSRV.audiences().player(player));
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.identity = Identity.identity(player.getUniqueId());
|
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
|
@Override
|
||||||
@ -59,7 +46,7 @@ public class BungeePlayer implements IPlayer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull String username() {
|
public @NotNull String username() {
|
||||||
return player.getName();
|
return commandSender.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -72,8 +59,4 @@ public class BungeePlayer implements IPlayer {
|
|||||||
return BungeeComponentUtil.fromLegacy(player.getDisplayName());
|
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:mcdiscordreserializer:4.2.4-SNAPSHOT'
|
||||||
runtimeDownloadApi 'dev.vankka:enhancedlegacytext:1.0.0-SNAPSHOT'
|
runtimeDownloadApi 'dev.vankka:enhancedlegacytext:1.0.0-SNAPSHOT'
|
||||||
|
|
||||||
|
// Brigadier
|
||||||
|
compileOnlyApi 'com.mojang:brigadier:1.0.18'
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
hikari('com.zaxxer:HikariCP:4.0.3') { exclude group: 'org.slf4j' }
|
hikari('com.zaxxer:HikariCP:4.0.3') { exclude group: 'org.slf4j' }
|
||||||
h2Driver 'com.h2database:h2:2.1.210'
|
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.discord.connection.DiscordConnectionDetails;
|
||||||
import com.discordsrv.api.event.bus.EventBus;
|
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.event.events.lifecycle.DiscordSRVShuttingDownEvent;
|
||||||
import com.discordsrv.api.module.type.Module;
|
import com.discordsrv.api.module.type.Module;
|
||||||
import com.discordsrv.common.api.util.ApiInstanceUtil;
|
import com.discordsrv.common.api.util.ApiInstanceUtil;
|
||||||
import com.discordsrv.common.channel.ChannelConfigHelper;
|
import com.discordsrv.common.channel.ChannelConfigHelper;
|
||||||
import com.discordsrv.common.channel.ChannelUpdaterModule;
|
import com.discordsrv.common.channel.ChannelUpdaterModule;
|
||||||
import com.discordsrv.common.channel.GlobalChannelLookupModule;
|
import com.discordsrv.common.channel.GlobalChannelLookupModule;
|
||||||
|
import com.discordsrv.common.command.game.GameCommandModule;
|
||||||
import com.discordsrv.common.component.ComponentFactory;
|
import com.discordsrv.common.component.ComponentFactory;
|
||||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||||
import com.discordsrv.common.config.main.LinkedAccountConfig;
|
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.function.CheckedRunnable;
|
||||||
import com.discordsrv.common.groupsync.GroupSyncModule;
|
import com.discordsrv.common.groupsync.GroupSyncModule;
|
||||||
import com.discordsrv.common.integration.LuckPermsIntegration;
|
import com.discordsrv.common.integration.LuckPermsIntegration;
|
||||||
|
import com.discordsrv.common.invite.DiscordInviteModule;
|
||||||
import com.discordsrv.common.linking.LinkProvider;
|
import com.discordsrv.common.linking.LinkProvider;
|
||||||
import com.discordsrv.common.linking.impl.MemoryLinker;
|
import com.discordsrv.common.linking.impl.MemoryLinker;
|
||||||
import com.discordsrv.common.linking.impl.StorageLinker;
|
import com.discordsrv.common.linking.impl.StorageLinker;
|
||||||
@ -69,6 +73,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import javax.annotation.OverridingMethodsMustInvokeSuper;
|
import javax.annotation.OverridingMethodsMustInvokeSuper;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -96,11 +101,12 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
|||||||
// DiscordSRV
|
// DiscordSRV
|
||||||
private DiscordSRVLogger logger;
|
private DiscordSRVLogger logger;
|
||||||
private ModuleManager moduleManager;
|
private ModuleManager moduleManager;
|
||||||
|
private JDAConnectionManager discordConnectionManager;
|
||||||
private ChannelConfigHelper channelConfig;
|
private ChannelConfigHelper channelConfig;
|
||||||
|
|
||||||
private Storage storage;
|
private Storage storage;
|
||||||
|
private boolean hikariLoaded = false;
|
||||||
private LinkProvider linkProvider;
|
private LinkProvider linkProvider;
|
||||||
private DiscordConnectionManager discordConnectionManager;
|
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
private final ReentrantLock lifecycleLock = new ReentrantLock();
|
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.componentFactory = new ComponentFactory(this);
|
||||||
this.discordAPI = new DiscordAPIImpl(this);
|
this.discordAPI = new DiscordAPIImpl(this);
|
||||||
this.discordConnectionDetails = new DiscordConnectionDetailsImpl(this);
|
this.discordConnectionDetails = new DiscordConnectionDetailsImpl(this);
|
||||||
|
this.discordConnectionManager = new JDAConnectionManager(this);
|
||||||
this.channelConfig = new ChannelConfigHelper(this);
|
this.channelConfig = new ChannelConfigHelper(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,9 +232,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected <T extends DiscordSRV> void registerModule(CheckedFunction<T, AbstractModule<?>> function) {
|
protected <T extends DiscordSRV> void registerModule(CheckedFunction<T, AbstractModule<?>> function) {
|
||||||
try {
|
moduleManager.registerModule((T) this, function);
|
||||||
registerModule(function.apply((T) this));
|
|
||||||
} catch (Throwable ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -249,6 +254,9 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
|||||||
this.status.set(status);
|
this.status.set(status);
|
||||||
this.status.notifyAll();
|
this.status.notifyAll();
|
||||||
}
|
}
|
||||||
|
if (status == Status.CONNECTED) {
|
||||||
|
eventBus().publish(new DiscordSRVConnectedEvent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -299,7 +307,11 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final CompletableFuture<Void> invokeEnable() {
|
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
|
@Override
|
||||||
@ -308,8 +320,8 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final CompletableFuture<Void> invokeReload() {
|
public final CompletableFuture<Void> invokeReload(Set<ReloadFlag> flags, boolean silent) {
|
||||||
return invoke(this::reload, "Failed to reload", false);
|
return invoke(() -> reload(flags, silent), "Failed to reload", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OverridingMethodsMustInvokeSuper
|
@OverridingMethodsMustInvokeSuper
|
||||||
@ -323,76 +335,16 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
|||||||
// Logging
|
// Logging
|
||||||
DependencyLoggerAdapter.setAppender(new DependencyLoggingHandler(this));
|
DependencyLoggerAdapter.setAppender(new DependencyLoggingHandler(this));
|
||||||
|
|
||||||
// Config
|
// Register PlayerProvider listeners
|
||||||
try {
|
playerProvider().subscribe();
|
||||||
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();
|
|
||||||
|
|
||||||
// Placeholder result stringifiers & global contexts
|
// Placeholder result stringifiers & global contexts
|
||||||
placeholderService().addResultMapper(new ComponentResultStringifier(this));
|
placeholderService().addResultMapper(new ComponentResultStringifier(this));
|
||||||
placeholderService().addGlobalContext(new GlobalTextHandlingContext(this));
|
placeholderService().addGlobalContext(new GlobalTextHandlingContext(this));
|
||||||
|
|
||||||
// Register PlayerProvider listeners
|
// Modules
|
||||||
playerProvider().subscribe();
|
|
||||||
|
|
||||||
registerModule(ChannelUpdaterModule::new);
|
registerModule(ChannelUpdaterModule::new);
|
||||||
|
registerModule(GameCommandModule::new);
|
||||||
registerModule(GlobalChannelLookupModule::new);
|
registerModule(GlobalChannelLookupModule::new);
|
||||||
registerModule(DiscordAPIEventModule::new);
|
registerModule(DiscordAPIEventModule::new);
|
||||||
registerModule(GroupSyncModule::new);
|
registerModule(GroupSyncModule::new);
|
||||||
@ -401,6 +353,14 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
|||||||
registerModule(DiscordMessageMirroringModule::new);
|
registerModule(DiscordMessageMirroringModule::new);
|
||||||
registerModule(JoinMessageModule::new);
|
registerModule(JoinMessageModule::new);
|
||||||
registerModule(LeaveMessageModule::new);
|
registerModule(LeaveMessageModule::new);
|
||||||
|
registerModule(DiscordInviteModule::new);
|
||||||
|
|
||||||
|
// Initial load
|
||||||
|
try {
|
||||||
|
invokeReload(ReloadFlag.ALL, true).get();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw e.getCause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private StorageType getStorageType() {
|
private StorageType getStorageType() {
|
||||||
@ -425,7 +385,102 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OverridingMethodsMustInvokeSuper
|
@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.DiscordSRVApi;
|
||||||
import com.discordsrv.api.module.type.Module;
|
import com.discordsrv.api.module.type.Module;
|
||||||
import com.discordsrv.common.channel.ChannelConfigHelper;
|
import com.discordsrv.common.channel.ChannelConfigHelper;
|
||||||
|
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||||
import com.discordsrv.common.component.ComponentFactory;
|
import com.discordsrv.common.component.ComponentFactory;
|
||||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||||
import com.discordsrv.common.config.main.MainConfig;
|
import com.discordsrv.common.config.main.MainConfig;
|
||||||
@ -47,7 +48,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Locale;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ public interface DiscordSRV extends DiscordSRVApi {
|
|||||||
PluginManager pluginManager();
|
PluginManager pluginManager();
|
||||||
OnlineMode onlineMode();
|
OnlineMode onlineMode();
|
||||||
ClasspathAppender classpathAppender();
|
ClasspathAppender classpathAppender();
|
||||||
|
ICommandHandler commandHandler();
|
||||||
@NotNull AbstractPlayerProvider<?, ?> playerProvider();
|
@NotNull AbstractPlayerProvider<?, ?> playerProvider();
|
||||||
|
|
||||||
// DiscordSRVApi
|
// DiscordSRVApi
|
||||||
@ -126,6 +128,6 @@ public interface DiscordSRV extends DiscordSRVApi {
|
|||||||
// Lifecycle
|
// Lifecycle
|
||||||
CompletableFuture<Void> invokeEnable();
|
CompletableFuture<Void> invokeEnable();
|
||||||
CompletableFuture<Void> invokeDisable();
|
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.DiscordMessageChannel;
|
||||||
import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel;
|
import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel;
|
||||||
import com.discordsrv.api.discord.api.entity.channel.DiscordThreadChannel;
|
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.channel.GameChannelLookupEvent;
|
||||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadEvent;
|
|
||||||
import com.discordsrv.common.DiscordSRV;
|
import com.discordsrv.common.DiscordSRV;
|
||||||
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
|
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
|
||||||
import com.discordsrv.common.config.main.channels.base.ChannelConfig;
|
import com.discordsrv.common.config.main.channels.base.ChannelConfig;
|
||||||
@ -94,16 +92,9 @@ public class ChannelConfigHelper {
|
|||||||
}
|
}
|
||||||
return map;
|
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<>();
|
Map<Long, Map<String, BaseChannelConfig>> newMap = new HashMap<>();
|
||||||
for (Map.Entry<String, BaseChannelConfig> entry : channels().entrySet()) {
|
for (Map.Entry<String, BaseChannelConfig> entry : channels().entrySet()) {
|
||||||
String channelName = entry.getKey();
|
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")
|
@Comment("Configuration options for group-role synchronization")
|
||||||
public GroupSyncConfig groupSync = new GroupSyncConfig();
|
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"
|
@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"
|
+ "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")
|
+ "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;
|
package com.discordsrv.common.dependency;
|
||||||
|
|
||||||
|
import com.discordsrv.api.DiscordSRVApi;
|
||||||
import com.discordsrv.common.DiscordSRV;
|
import com.discordsrv.common.DiscordSRV;
|
||||||
import com.discordsrv.common.logging.Logger;
|
import com.discordsrv.common.logging.Logger;
|
||||||
import com.discordsrv.common.scheduler.threadfactory.CountingForkJoinWorkerThreadFactory;
|
import com.discordsrv.common.scheduler.threadfactory.CountingForkJoinWorkerThreadFactory;
|
||||||
@ -91,7 +92,7 @@ public class InitialDependencyLoader {
|
|||||||
if (discordSRV == null) {
|
if (discordSRV == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
discordSRV.invokeReload();
|
discordSRV.invokeReload(DiscordSRVApi.ReloadFlag.DEFAULT_FLAGS, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disable(DiscordSRV discordSRV) {
|
public void disable(DiscordSRV discordSRV) {
|
||||||
|
@ -69,7 +69,11 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
|||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
synchronized (pairs) {
|
synchronized (pairs) {
|
||||||
pairs.values().forEach(future -> future.cancel(false));
|
pairs.values().forEach(future -> {
|
||||||
|
if (future != null) {
|
||||||
|
future.cancel(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
pairs.clear();
|
pairs.clear();
|
||||||
groupsToPairs.clear();
|
groupsToPairs.clear();
|
||||||
rolesToPairs.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) {
|
public StorageLinker(DiscordSRV discordSRV) {
|
||||||
super(discordSRV);
|
super(discordSRV);
|
||||||
discordSRV.logger().info("Using storage for linked accounts");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,13 +20,16 @@ package com.discordsrv.common.module;
|
|||||||
|
|
||||||
import com.discordsrv.api.event.bus.EventPriority;
|
import com.discordsrv.api.event.bus.EventPriority;
|
||||||
import com.discordsrv.api.event.bus.Subscribe;
|
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.event.events.lifecycle.DiscordSRVShuttingDownEvent;
|
||||||
import com.discordsrv.api.module.type.Module;
|
import com.discordsrv.api.module.type.Module;
|
||||||
import com.discordsrv.common.DiscordSRV;
|
import com.discordsrv.common.DiscordSRV;
|
||||||
import com.discordsrv.common.debug.DebugGenerateEvent;
|
import com.discordsrv.common.debug.DebugGenerateEvent;
|
||||||
import com.discordsrv.common.debug.file.TextDebugFile;
|
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.AbstractModule;
|
||||||
|
import com.discordsrv.common.module.type.ModuleDelegate;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -39,13 +42,20 @@ public class ModuleManager {
|
|||||||
|
|
||||||
private final Set<Module> modules = new CopyOnWriteArraySet<>();
|
private final Set<Module> modules = new CopyOnWriteArraySet<>();
|
||||||
private final Map<String, Module> moduleLookupTable = new ConcurrentHashMap<>();
|
private final Map<String, Module> moduleLookupTable = new ConcurrentHashMap<>();
|
||||||
|
private final Map<Module, AbstractModule<?>> delegates = new ConcurrentHashMap<>();
|
||||||
private final DiscordSRV discordSRV;
|
private final DiscordSRV discordSRV;
|
||||||
|
private final Logger logger;
|
||||||
|
|
||||||
public ModuleManager(DiscordSRV discordSRV) {
|
public ModuleManager(DiscordSRV discordSRV) {
|
||||||
this.discordSRV = discordSRV;
|
this.discordSRV = discordSRV;
|
||||||
|
this.logger = new NamedLogger(discordSRV, "MODULE_MANAGER");
|
||||||
discordSRV.eventBus().subscribe(this);
|
discordSRV.eventBus().subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Logger logger() {
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T extends Module> T getModule(Class<T> moduleType) {
|
public <T extends Module> T getModule(Class<T> moduleType) {
|
||||||
return (T) moduleLookupTable.computeIfAbsent(moduleType.getName(), key -> {
|
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.modules.add(module);
|
||||||
this.moduleLookupTable.put(module.getClass().getName(), 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 {
|
try {
|
||||||
if (module instanceof AbstractModule) {
|
if (module.enableModule()) {
|
||||||
((AbstractModule<?>) module).enableModule();
|
logger.debug(module + " enabled");
|
||||||
} else if (enableNonAbstract) {
|
|
||||||
module.enable();
|
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} 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) {
|
public void unregister(Module module) {
|
||||||
this.modules.remove(module);
|
if (module instanceof ModuleDelegate) {
|
||||||
this.moduleLookupTable.values().removeIf(mod -> mod == module);
|
throw new IllegalArgumentException("Cannot unregister a delegate");
|
||||||
|
}
|
||||||
|
|
||||||
disable(module);
|
disable(module);
|
||||||
|
|
||||||
|
this.modules.remove(module);
|
||||||
|
this.moduleLookupTable.values().removeIf(mod -> mod == module);
|
||||||
|
this.delegates.remove(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disable(Module module) {
|
private void disable(Module module) {
|
||||||
|
AbstractModule<?> abstractModule = getAbstract(module);
|
||||||
try {
|
try {
|
||||||
module.disable();
|
logger.debug(module + " disabling");
|
||||||
|
abstractModule.disable();
|
||||||
} catch (Throwable t) {
|
} 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 reload() {
|
||||||
public void onReload(DiscordSRVReloadEvent event) {
|
|
||||||
for (Module module : modules) {
|
for (Module module : modules) {
|
||||||
|
AbstractModule<?> abstractModule = getAbstract(module);
|
||||||
|
|
||||||
// Check if the module needs to be enabled due to reload
|
// Check if the module needs to be enabled due to reload
|
||||||
enable(module, false);
|
enable(abstractModule);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
module.reload();
|
abstractModule.reload();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
discordSRV.logger().error("Failed to reload " + module.getClass().getSimpleName(), t);
|
discordSRV.logger().error("Failed to reload " + module.getClass().getSimpleName(), t);
|
||||||
}
|
}
|
||||||
@ -124,11 +164,13 @@ public class ModuleManager {
|
|||||||
builder.append("Enabled modules:");
|
builder.append("Enabled modules:");
|
||||||
List<Module> disabled = new ArrayList<>();
|
List<Module> disabled = new ArrayList<>();
|
||||||
for (Module module : modules) {
|
for (Module module : modules) {
|
||||||
if (!module.isEnabled()) {
|
AbstractModule<?> abstractModule = getAbstract(module);
|
||||||
disabled.add(module);
|
|
||||||
|
if (!abstractModule.isEnabled()) {
|
||||||
|
disabled.add(abstractModule);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
appendModule(builder, module);
|
appendModule(builder, abstractModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append("\n\nDisabled modules:");
|
builder.append("\n\nDisabled modules:");
|
||||||
|
@ -44,9 +44,9 @@ public abstract class AbstractModule<DT extends DiscordSRV> implements Module {
|
|||||||
return logger;
|
return logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void enableModule() {
|
public final boolean enableModule() {
|
||||||
if (hasBeenEnabled || !isEnabled()) {
|
if (hasBeenEnabled || !isEnabled()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasBeenEnabled = true;
|
hasBeenEnabled = true;
|
||||||
@ -56,6 +56,12 @@ public abstract class AbstractModule<DT extends DiscordSRV> implements Module {
|
|||||||
discordSRV.eventBus().subscribe(this);
|
discordSRV.eventBus().subscribe(this);
|
||||||
// Ignore not having listener methods exception
|
// Ignore not having listener methods exception
|
||||||
} catch (IllegalArgumentException ignored) {}
|
} catch (IllegalArgumentException ignored) {}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getName() + "{enabled=" + isEnabled() + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility
|
// 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;
|
package com.discordsrv.common;
|
||||||
|
|
||||||
|
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||||
import com.discordsrv.common.config.main.MainConfig;
|
import com.discordsrv.common.config.main.MainConfig;
|
||||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
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.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionConfig> {
|
public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionConfig> {
|
||||||
|
|
||||||
public static final MockDiscordSRV INSTANCE = new MockDiscordSRV();
|
public static final MockDiscordSRV INSTANCE = new MockDiscordSRV();
|
||||||
@ -102,6 +104,11 @@ public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionCon
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICommandHandler commandHandler() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull AbstractPlayerProvider<?, ?> playerProvider() {
|
public @NotNull AbstractPlayerProvider<?, ?> playerProvider() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package com.discordsrv.config;
|
package com.discordsrv.config;
|
||||||
|
|
||||||
import com.discordsrv.common.AbstractDiscordSRV;
|
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.connection.ConnectionConfig;
|
||||||
import com.discordsrv.common.config.main.MainConfig;
|
import com.discordsrv.common.config.main.MainConfig;
|
||||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
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.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionConfig> {
|
public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionConfig> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -77,6 +79,11 @@ public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionCon
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICommandHandler commandHandler() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull AbstractPlayerProvider<?, ?> playerProvider() {
|
public @NotNull AbstractPlayerProvider<?, ?> playerProvider() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
apply from: rootProject.file('buildscript/runtime.gradle')
|
apply from: rootProject.file('buildscript/runtime.gradle')
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
archiveFileName = 'sponge.jarinjar'
|
||||||
|
|
||||||
|
// Relocations in buildscript/relocations.gradle
|
||||||
|
}
|
||||||
|
|
||||||
var spongeVersion = '8.0.0'
|
var spongeVersion = '8.0.0'
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
@ -31,9 +37,3 @@ dependencies {
|
|||||||
implementation 'dev.vankka:dependencydownload-jarinjar-bootstrap:' + rootProject.ddVersion
|
implementation 'dev.vankka:dependencydownload-jarinjar-bootstrap:' + rootProject.ddVersion
|
||||||
compileOnlyApi project(':sponge:sponge-loader')
|
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.Logger;
|
||||||
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
||||||
import com.discordsrv.sponge.bootstrap.ISpongeBootstrap;
|
import com.discordsrv.sponge.bootstrap.ISpongeBootstrap;
|
||||||
|
import com.discordsrv.sponge.command.game.handler.SpongeCommandHandler;
|
||||||
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
|
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
|
||||||
import dev.vankka.mcdependencydownload.bootstrap.AbstractBootstrap;
|
import dev.vankka.mcdependencydownload.bootstrap.AbstractBootstrap;
|
||||||
import dev.vankka.mcdependencydownload.bootstrap.classpath.JarInJarClasspathAppender;
|
import dev.vankka.mcdependencydownload.bootstrap.classpath.JarInJarClasspathAppender;
|
||||||
@ -39,6 +40,7 @@ public class DiscordSRVSpongeBootstrap extends AbstractBootstrap implements ISpo
|
|||||||
private final ClasspathAppender classpathAppender;
|
private final ClasspathAppender classpathAppender;
|
||||||
private final InitialDependencyLoader dependencies;
|
private final InitialDependencyLoader dependencies;
|
||||||
private SpongeDiscordSRV discordSRV;
|
private SpongeDiscordSRV discordSRV;
|
||||||
|
private SpongeCommandHandler commandHandler;
|
||||||
|
|
||||||
private final PluginContainer pluginContainer;
|
private final PluginContainer pluginContainer;
|
||||||
private final Game game;
|
private final Game game;
|
||||||
@ -63,11 +65,14 @@ public class DiscordSRVSpongeBootstrap extends AbstractBootstrap implements ISpo
|
|||||||
@Override
|
@Override
|
||||||
public void onConstruct() {
|
public void onConstruct() {
|
||||||
dependencies.load();
|
dependencies.load();
|
||||||
|
|
||||||
|
this.commandHandler = new SpongeCommandHandler(() -> discordSRV, pluginContainer);
|
||||||
|
game.eventManager().registerListeners(pluginContainer, commandHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStarted() {
|
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) {
|
if (discordSRV != null) {
|
||||||
discordSRV.invokeServerStarted();
|
discordSRV.invokeServerStarted();
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package com.discordsrv.sponge;
|
package com.discordsrv.sponge;
|
||||||
|
|
||||||
import com.discordsrv.api.DiscordSRVApi;
|
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.connection.ConnectionConfig;
|
||||||
import com.discordsrv.common.config.main.MainConfig;
|
import com.discordsrv.common.config.main.MainConfig;
|
||||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
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.logging.Logger;
|
||||||
import com.discordsrv.common.plugin.PluginManager;
|
import com.discordsrv.common.plugin.PluginManager;
|
||||||
import com.discordsrv.common.server.ServerDiscordSRV;
|
import com.discordsrv.common.server.ServerDiscordSRV;
|
||||||
|
import com.discordsrv.sponge.command.game.handler.SpongeCommandHandler;
|
||||||
import com.discordsrv.sponge.console.SpongeConsole;
|
import com.discordsrv.sponge.console.SpongeConsole;
|
||||||
import com.discordsrv.sponge.player.SpongePlayerProvider;
|
import com.discordsrv.sponge.player.SpongePlayerProvider;
|
||||||
import com.discordsrv.sponge.plugin.SpongePluginManager;
|
import com.discordsrv.sponge.plugin.SpongePluginManager;
|
||||||
@ -52,8 +54,16 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
|
|||||||
private final SpongeConsole console;
|
private final SpongeConsole console;
|
||||||
private final SpongePlayerProvider playerProvider;
|
private final SpongePlayerProvider playerProvider;
|
||||||
private final SpongePluginManager pluginManager;
|
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.logger = logger;
|
||||||
this.classpathAppender = classpathAppender;
|
this.classpathAppender = classpathAppender;
|
||||||
this.dataDirectory = dataDirectory;
|
this.dataDirectory = dataDirectory;
|
||||||
@ -64,6 +74,7 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
|
|||||||
this.console = new SpongeConsole(this);
|
this.console = new SpongeConsole(this);
|
||||||
this.playerProvider = new SpongePlayerProvider(this);
|
this.playerProvider = new SpongePlayerProvider(this);
|
||||||
this.pluginManager = new SpongePluginManager(this);
|
this.pluginManager = new SpongePluginManager(this);
|
||||||
|
this.commandHandler = commandHandler;
|
||||||
|
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
@ -123,6 +134,11 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
|
|||||||
return classpathAppender;
|
return classpathAppender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICommandHandler commandHandler() {
|
||||||
|
return commandHandler;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
|
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
|
||||||
return null;
|
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.LoggingBackend;
|
||||||
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
||||||
import com.discordsrv.sponge.SpongeDiscordSRV;
|
import com.discordsrv.sponge.SpongeDiscordSRV;
|
||||||
import net.kyori.adventure.audience.Audience;
|
import com.discordsrv.sponge.command.game.sender.SpongeCommandSender;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.spongepowered.api.command.exception.CommandException;
|
|
||||||
|
|
||||||
public class SpongeConsole implements Console {
|
public class SpongeConsole extends SpongeCommandSender implements Console {
|
||||||
|
|
||||||
private final SpongeDiscordSRV discordSRV;
|
|
||||||
private final LoggingBackend loggingBackend;
|
private final LoggingBackend loggingBackend;
|
||||||
|
|
||||||
public SpongeConsole(SpongeDiscordSRV discordSRV) {
|
public SpongeConsole(SpongeDiscordSRV discordSRV) {
|
||||||
this.discordSRV = discordSRV;
|
super(discordSRV, () -> discordSRV.game().systemSubject(), () -> discordSRV.game().systemSubject());
|
||||||
this.loggingBackend = Log4JLoggerImpl.getRoot();
|
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
|
@Override
|
||||||
public LoggingBackend loggingBackend() {
|
public LoggingBackend loggingBackend() {
|
||||||
return 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.DiscordSRV;
|
||||||
import com.discordsrv.common.player.IPlayer;
|
import com.discordsrv.common.player.IPlayer;
|
||||||
import com.discordsrv.sponge.SpongeDiscordSRV;
|
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 net.kyori.adventure.text.Component;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.spongepowered.api.command.exception.CommandException;
|
|
||||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
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 ServerPlayer player;
|
||||||
|
private final Identity identity;
|
||||||
|
|
||||||
public SpongePlayer(SpongeDiscordSRV discordSRV, ServerPlayer player) {
|
public SpongePlayer(SpongeDiscordSRV discordSRV, ServerPlayer player) {
|
||||||
super(discordSRV, player.user());
|
super(discordSRV, () -> player, () -> player);
|
||||||
this.player = player;
|
this.player = player;
|
||||||
}
|
this.identity = Identity.identity(player.uniqueId());
|
||||||
|
|
||||||
@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) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -53,13 +43,18 @@ public class SpongePlayer extends SpongeOfflinePlayer implements IPlayer {
|
|||||||
return discordSRV;
|
return discordSRV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String username() {
|
||||||
|
return player.name();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Component displayName() {
|
public @NotNull Component displayName() {
|
||||||
return player.displayName().get();
|
return player.displayName().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Audience audience() {
|
public @NotNull Identity identity() {
|
||||||
return player;
|
return identity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ package com.discordsrv.sponge.player;
|
|||||||
import com.discordsrv.common.player.IOfflinePlayer;
|
import com.discordsrv.common.player.IOfflinePlayer;
|
||||||
import com.discordsrv.common.server.player.ServerPlayerProvider;
|
import com.discordsrv.common.server.player.ServerPlayerProvider;
|
||||||
import com.discordsrv.sponge.SpongeDiscordSRV;
|
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.User;
|
||||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||||
import org.spongepowered.api.event.Listener;
|
import org.spongepowered.api.event.Listener;
|
||||||
@ -64,7 +63,7 @@ public class SpongePlayerProvider extends ServerPlayerProvider<SpongePlayer, Spo
|
|||||||
removePlayer(event.player().uniqueId());
|
removePlayer(event.player().uniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpongePlayer player(Player player) {
|
public SpongePlayer player(ServerPlayer player) {
|
||||||
return player(player.uniqueId()).orElseThrow(() -> new IllegalStateException("Player not available"));
|
return player(player.uniqueId()).orElseThrow(() -> new IllegalStateException("Player not available"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
apply from: rootProject.file('buildscript/standalone.gradle')
|
apply from: rootProject.file('buildscript/standalone.gradle')
|
||||||
apply plugin: 'net.kyori.blossom'
|
apply plugin: 'net.kyori.blossom'
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
archiveBaseName = 'DiscordSRV-Velocity'
|
||||||
|
|
||||||
|
// Relocations in buildscript/relocations.gradle
|
||||||
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
exclusiveContent {
|
exclusiveContent {
|
||||||
forRepository {
|
forRepository {
|
||||||
@ -33,12 +39,6 @@ jar {
|
|||||||
dependsOn blossomSourceReplacementJava
|
dependsOn blossomSourceReplacementJava
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
archiveBaseName = 'DiscordSRV-Velocity'
|
|
||||||
|
|
||||||
// Relocations in buildscript/relocations.gradle
|
|
||||||
}
|
|
||||||
|
|
||||||
blossom {
|
blossom {
|
||||||
var mainClass = 'src/main/java/com/discordsrv/velocity/DiscordSRVVelocityBootstrap.java'
|
var mainClass = 'src/main/java/com/discordsrv/velocity/DiscordSRVVelocityBootstrap.java'
|
||||||
replaceToken('@VERSION@', project.version, mainClass)
|
replaceToken('@VERSION@', project.version, mainClass)
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package com.discordsrv.velocity;
|
package com.discordsrv.velocity;
|
||||||
|
|
||||||
|
import com.discordsrv.common.command.game.handler.ICommandHandler;
|
||||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||||
import com.discordsrv.common.config.main.MainConfig;
|
import com.discordsrv.common.config.main.MainConfig;
|
||||||
import com.discordsrv.common.config.manager.ConnectionConfigManager;
|
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.plugin.PluginManager;
|
||||||
import com.discordsrv.common.scheduler.StandardScheduler;
|
import com.discordsrv.common.scheduler.StandardScheduler;
|
||||||
import com.discordsrv.proxy.ProxyDiscordSRV;
|
import com.discordsrv.proxy.ProxyDiscordSRV;
|
||||||
|
import com.discordsrv.velocity.command.game.handler.VelocityCommandHandler;
|
||||||
import com.discordsrv.velocity.console.VelocityConsole;
|
import com.discordsrv.velocity.console.VelocityConsole;
|
||||||
import com.discordsrv.velocity.player.VelocityPlayerProvider;
|
import com.discordsrv.velocity.player.VelocityPlayerProvider;
|
||||||
import com.discordsrv.velocity.plugin.VelocityPluginManager;
|
import com.discordsrv.velocity.plugin.VelocityPluginManager;
|
||||||
@ -50,6 +52,7 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
|
|||||||
private final VelocityConsole console;
|
private final VelocityConsole console;
|
||||||
private final VelocityPlayerProvider playerProvider;
|
private final VelocityPlayerProvider playerProvider;
|
||||||
private final VelocityPluginManager pluginManager;
|
private final VelocityPluginManager pluginManager;
|
||||||
|
private final VelocityCommandHandler commandHandler;
|
||||||
|
|
||||||
public VelocityDiscordSRV(Object plugin, Logger logger, ClasspathAppender classpathAppender, ProxyServer proxyServer, PluginContainer pluginContainer, Path dataDirectory) {
|
public VelocityDiscordSRV(Object plugin, Logger logger, ClasspathAppender classpathAppender, ProxyServer proxyServer, PluginContainer pluginContainer, Path dataDirectory) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
@ -63,6 +66,7 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
|
|||||||
this.console = new VelocityConsole(this);
|
this.console = new VelocityConsole(this);
|
||||||
this.playerProvider = new VelocityPlayerProvider(this);
|
this.playerProvider = new VelocityPlayerProvider(this);
|
||||||
this.pluginManager = new VelocityPluginManager(this);
|
this.pluginManager = new VelocityPluginManager(this);
|
||||||
|
this.commandHandler = new VelocityCommandHandler(this);
|
||||||
|
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
@ -124,6 +128,11 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
|
|||||||
return classpathAppender;
|
return classpathAppender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICommandHandler commandHandler() {
|
||||||
|
return commandHandler;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
|
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
|
||||||
return null;
|
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.LoggingBackend;
|
||||||
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
|
||||||
import com.discordsrv.velocity.VelocityDiscordSRV;
|
import com.discordsrv.velocity.VelocityDiscordSRV;
|
||||||
import net.kyori.adventure.audience.Audience;
|
import com.discordsrv.velocity.command.game.sender.VelocityCommandSender;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class VelocityConsole implements Console {
|
public class VelocityConsole extends VelocityCommandSender implements Console {
|
||||||
|
|
||||||
private final VelocityDiscordSRV discordSRV;
|
|
||||||
private final LoggingBackend loggingBackend;
|
private final LoggingBackend loggingBackend;
|
||||||
|
|
||||||
public VelocityConsole(VelocityDiscordSRV discordSRV) {
|
public VelocityConsole(VelocityDiscordSRV discordSRV) {
|
||||||
this.discordSRV = discordSRV;
|
super(discordSRV, discordSRV.proxy().getConsoleCommandSource());
|
||||||
this.loggingBackend = Log4JLoggerImpl.getRoot();
|
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
|
@Override
|
||||||
public LoggingBackend loggingBackend() {
|
public LoggingBackend loggingBackend() {
|
||||||
return 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.DiscordSRV;
|
||||||
import com.discordsrv.common.player.IPlayer;
|
import com.discordsrv.common.player.IPlayer;
|
||||||
import com.discordsrv.velocity.VelocityDiscordSRV;
|
import com.discordsrv.velocity.VelocityDiscordSRV;
|
||||||
|
import com.discordsrv.velocity.command.game.sender.VelocityCommandSender;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import net.kyori.adventure.audience.Audience;
|
|
||||||
import net.kyori.adventure.identity.Identity;
|
import net.kyori.adventure.identity.Identity;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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;
|
private final Player player;
|
||||||
|
|
||||||
public VelocityPlayer(VelocityDiscordSRV discordSRV, Player player) {
|
public VelocityPlayer(VelocityDiscordSRV discordSRV, Player player) {
|
||||||
this.discordSRV = discordSRV;
|
super(discordSRV, player);
|
||||||
this.player = 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
|
@Override
|
||||||
public DiscordSRV discordSRV() {
|
public DiscordSRV discordSRV() {
|
||||||
return discordSRV;
|
return discordSRV;
|
||||||
@ -70,9 +59,4 @@ public class VelocityPlayer implements IPlayer {
|
|||||||
() -> Component.text(player.getUsername())
|
() -> Component.text(player.getUsername())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Audience audience() {
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user