geforkt von Mirrors/Velocity
Update 1.21.2 client support #5
@ -45,7 +45,9 @@ public interface CommandManager {
|
||||
* @throws IllegalArgumentException if one of the given aliases is already registered, or
|
||||
* the given command does not implement a registrable {@link Command} subinterface
|
||||
* @see Command for a list of registrable Command subinterfaces
|
||||
* @deprecated use {@link #register(CommandMeta, Command)} instead with a plugin specified
|
||||
*/
|
||||
@Deprecated
|
||||
default void register(String alias, Command command, String... otherAliases) {
|
||||
register(metaBuilder(alias).aliases(otherAliases).build(), command);
|
||||
}
|
||||
@ -55,7 +57,9 @@ public interface CommandManager {
|
||||
*
|
||||
* @param command the command to register
|
||||
* @throws IllegalArgumentException if the node alias is already registered
|
||||
* @deprecated use {@link #register(CommandMeta, Command)} instead with a plugin specified
|
||||
*/
|
||||
@Deprecated
|
||||
void register(BrigadierCommand command);
|
||||
|
||||
/**
|
||||
|
@ -22,11 +22,13 @@ import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.velocitypowered.api.command.BrigadierCommand;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyReloadEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.plugin.PluginContainer;
|
||||
import com.velocitypowered.api.plugin.PluginDescription;
|
||||
import com.velocitypowered.api.plugin.PluginManager;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
@ -52,6 +54,9 @@ import com.velocitypowered.proxy.crypto.EncryptionUtils;
|
||||
import com.velocitypowered.proxy.event.VelocityEventManager;
|
||||
import com.velocitypowered.proxy.network.ConnectionManager;
|
||||
import com.velocitypowered.proxy.plugin.VelocityPluginManager;
|
||||
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
||||
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
|
||||
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.util.FaviconSerializer;
|
||||
import com.velocitypowered.proxy.protocol.util.GameProfileSerializer;
|
||||
@ -77,6 +82,7 @@ import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -111,6 +117,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
*/
|
||||
public class VelocityServer implements ProxyServer, ForwardingAudience {
|
||||
|
||||
public static final String VELOCITY_URL = "https://velocitypowered.com";
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(VelocityServer.class);
|
||||
public static final Gson GENERAL_GSON = new GsonBuilder()
|
||||
.registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE)
|
||||
@ -163,7 +171,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
||||
VelocityServer(final ProxyOptions options) {
|
||||
pluginManager = new VelocityPluginManager(this);
|
||||
eventManager = new VelocityEventManager(pluginManager);
|
||||
commandManager = new VelocityCommandManager(eventManager);
|
||||
commandManager = new VelocityCommandManager(eventManager, pluginManager);
|
||||
scheduler = new VelocityScheduler(pluginManager);
|
||||
console = new VelocityConsole(this);
|
||||
cm = new ConnectionManager(this);
|
||||
@ -200,6 +208,16 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
||||
return new ProxyVersion(implName, implVendor, implVersion);
|
||||
}
|
||||
|
||||
private VelocityPluginContainer createVirtualPlugin() {
|
||||
ProxyVersion version = getVersion();
|
||||
PluginDescription description = new VelocityPluginDescription(
|
||||
"velocity", version.getName(), version.getVersion(), "The Velocity proxy",
|
||||
VELOCITY_URL, ImmutableList.of(version.getVendor()), Collections.emptyList(), null);
|
||||
VelocityPluginContainer container = new VelocityPluginContainer(description);
|
||||
container.setInstance(VelocityVirtualPlugin.INSTANCE);
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VelocityCommandManager getCommandManager() {
|
||||
return commandManager;
|
||||
@ -214,6 +232,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
||||
void start() {
|
||||
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
|
||||
console.setupStreams();
|
||||
pluginManager.registerPlugin(this.createVirtualPlugin());
|
||||
|
||||
registerTranslations();
|
||||
|
||||
@ -222,11 +241,35 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
||||
cm.logChannelInformation();
|
||||
|
||||
// Initialize commands first
|
||||
commandManager.register(VelocityCommand.create(this));
|
||||
commandManager.register(CallbackCommand.create());
|
||||
commandManager.register(ServerCommand.create(this));
|
||||
commandManager.register("shutdown", ShutdownCommand.command(this),
|
||||
"end", "stop");
|
||||
final BrigadierCommand velocityParentCommand = VelocityCommand.create(this);
|
||||
commandManager.register(
|
||||
commandManager.metaBuilder(velocityParentCommand)
|
||||
.plugin(VelocityVirtualPlugin.INSTANCE)
|
||||
.build(),
|
||||
velocityParentCommand
|
||||
);
|
||||
final BrigadierCommand callbackCommand = CallbackCommand.create();
|
||||
commandManager.register(
|
||||
commandManager.metaBuilder(callbackCommand)
|
||||
.plugin(VelocityVirtualPlugin.INSTANCE)
|
||||
.build(),
|
||||
velocityParentCommand
|
||||
);
|
||||
final BrigadierCommand serverCommand = ServerCommand.create(this);
|
||||
commandManager.register(
|
||||
commandManager.metaBuilder(serverCommand)
|
||||
.plugin(VelocityVirtualPlugin.INSTANCE)
|
||||
.build(),
|
||||
serverCommand
|
||||
);
|
||||
final BrigadierCommand shutdownCommand = ShutdownCommand.command(this);
|
||||
commandManager.register(
|
||||
commandManager.metaBuilder(shutdownCommand)
|
||||
.plugin(VelocityVirtualPlugin.INSTANCE)
|
||||
.aliases("end", "stop")
|
||||
.build(),
|
||||
shutdownCommand
|
||||
);
|
||||
new GlistCommand(this).register();
|
||||
new SendCommand(this).register();
|
||||
|
||||
|
@ -20,6 +20,7 @@ package com.velocitypowered.proxy.command;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.ParseResults;
|
||||
@ -37,11 +38,15 @@ import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.command.VelocityBrigadierMessage;
|
||||
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
||||
import com.velocitypowered.api.event.command.PostCommandInvocationEvent;
|
||||
import com.velocitypowered.api.plugin.PluginManager;
|
||||
import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper;
|
||||
import com.velocitypowered.proxy.command.registrar.BrigadierCommandRegistrar;
|
||||
import com.velocitypowered.proxy.command.registrar.CommandRegistrar;
|
||||
import com.velocitypowered.proxy.command.registrar.RawCommandRegistrar;
|
||||
import com.velocitypowered.proxy.command.registrar.SimpleCommandRegistrar;
|
||||
import com.velocitypowered.proxy.event.VelocityEventManager;
|
||||
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
|
||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -49,8 +54,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.stream.Collectors;
|
||||
@ -73,14 +77,16 @@ public class VelocityCommandManager implements CommandManager {
|
||||
private final SuggestionsProvider<CommandSource> suggestionsProvider;
|
||||
private final CommandGraphInjector<CommandSource> injector;
|
||||
private final Map<String, CommandMeta> commandMetas;
|
||||
private final ExecutorService asyncExecutor;
|
||||
private final PluginManager pluginManager;
|
||||
|
||||
/**
|
||||
* Constructs a command manager.
|
||||
*
|
||||
* @param eventManager the event manager
|
||||
*/
|
||||
public VelocityCommandManager(final VelocityEventManager eventManager) {
|
||||
public VelocityCommandManager(final VelocityEventManager eventManager,
|
||||
PluginManager pluginManager) {
|
||||
this.pluginManager = pluginManager;
|
||||
this.lock = new ReentrantReadWriteLock();
|
||||
this.dispatcher = new CommandDispatcher<>();
|
||||
this.eventManager = Preconditions.checkNotNull(eventManager);
|
||||
@ -92,7 +98,6 @@ public class VelocityCommandManager implements CommandManager {
|
||||
this.suggestionsProvider = new SuggestionsProvider<>(this.dispatcher, this.lock.readLock());
|
||||
this.injector = new CommandGraphInjector<>(this.dispatcher, this.lock.readLock());
|
||||
this.commandMetas = new ConcurrentHashMap<>();
|
||||
this.asyncExecutor = ForkJoinPool.commonPool(); // TODO: remove entirely
|
||||
}
|
||||
|
||||
public void setAnnounceProxyCommands(boolean announceProxyCommands) {
|
||||
@ -222,16 +227,13 @@ public class VelocityCommandManager implements CommandManager {
|
||||
return eventManager.fire(new CommandExecuteEvent(source, cmdLine));
|
||||
}
|
||||
|
||||
private boolean executeImmediately0(final CommandSource source, final String cmdLine) {
|
||||
private boolean executeImmediately0(final CommandSource source, final ParseResults<CommandSource> parsed) {
|
||||
Preconditions.checkNotNull(source, "source");
|
||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||
|
||||
final String normalizedInput = VelocityCommands.normalizeInput(cmdLine, true);
|
||||
CommandResult result = CommandResult.EXCEPTION;
|
||||
try {
|
||||
// The parse can fail if the requirement predicates throw
|
||||
final ParseResults<CommandSource> parse = this.parse(normalizedInput, source);
|
||||
boolean executed = dispatcher.execute(parse) != BrigadierCommand.FORWARD;
|
||||
boolean executed = dispatcher.execute(parsed) != BrigadierCommand.FORWARD;
|
||||
result = executed ? CommandResult.EXECUTED : CommandResult.FORWARDED;
|
||||
return executed;
|
||||
} catch (final CommandSyntaxException e) {
|
||||
@ -253,9 +255,9 @@ public class VelocityCommandManager implements CommandManager {
|
||||
}
|
||||
} catch (final Throwable e) {
|
||||
// Ugly, ugly swallowing of everything Throwable, because plugins are naughty.
|
||||
throw new RuntimeException("Unable to invoke command " + cmdLine + " for " + source, e);
|
||||
throw new RuntimeException("Unable to invoke command " + parsed.getReader().getString() + "for " + source, e);
|
||||
} finally {
|
||||
eventManager.fireAndForget(new PostCommandInvocationEvent(source, cmdLine, result));
|
||||
eventManager.fireAndForget(new PostCommandInvocationEvent(source, parsed.getReader().getString(), result));
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,13 +266,17 @@ public class VelocityCommandManager implements CommandManager {
|
||||
Preconditions.checkNotNull(source, "source");
|
||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||
|
||||
return callCommandEvent(source, cmdLine).thenApplyAsync(event -> {
|
||||
return callCommandEvent(source, cmdLine).thenComposeAsync(event -> {
|
||||
CommandExecuteEvent.CommandResult commandResult = event.getResult();
|
||||
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
|
||||
return false;
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
return executeImmediately0(source, commandResult.getCommand().orElse(event.getCommand()));
|
||||
}, asyncExecutor);
|
||||
final ParseResults<CommandSource> parsed = this.parse(
|
||||
commandResult.getCommand().orElse(cmdLine), source);
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> executeImmediately0(source, parsed), this.getAsyncExecutor(parsed)
|
||||
);
|
||||
}, figureAsyncExecutorForParsing());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -279,7 +285,13 @@ public class VelocityCommandManager implements CommandManager {
|
||||
Preconditions.checkNotNull(source, "source");
|
||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> executeImmediately0(source, cmdLine), asyncExecutor);
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> this.parse(cmdLine, source), figureAsyncExecutorForParsing()
|
||||
).thenCompose(
|
||||
parsed -> CompletableFuture.supplyAsync(
|
||||
() -> executeImmediately0(source, parsed), this.getAsyncExecutor(parsed)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -327,9 +339,10 @@ public class VelocityCommandManager implements CommandManager {
|
||||
* @return the parse results
|
||||
*/
|
||||
private ParseResults<CommandSource> parse(final String input, final CommandSource source) {
|
||||
final String normalizedInput = VelocityCommands.normalizeInput(input, true);
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
return dispatcher.parse(input, source);
|
||||
return dispatcher.parse(normalizedInput, source);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
@ -373,4 +386,25 @@ public class VelocityCommandManager implements CommandManager {
|
||||
public CommandGraphInjector<CommandSource> getInjector() {
|
||||
return injector;
|
||||
}
|
||||
|
||||
private Executor getAsyncExecutor(ParseResults<CommandSource> parse) {
|
||||
Object registrant;
|
||||
if (parse.getContext().getCommand() instanceof VelocityBrigadierCommandWrapper vbcw) {
|
||||
registrant = vbcw.registrant() == null ? VelocityVirtualPlugin.INSTANCE : vbcw.registrant();
|
||||
} else {
|
||||
registrant = VelocityVirtualPlugin.INSTANCE;
|
||||
}
|
||||
return pluginManager.ensurePluginContainer(registrant).getExecutorService();
|
||||
}
|
||||
|
||||
private Executor figureAsyncExecutorForParsing() {
|
||||
final Thread thread = Thread.currentThread();
|
||||
if (thread instanceof FastThreadLocalThread) {
|
||||
// we *never* want to block the Netty event loop, so use the async executor
|
||||
return pluginManager.ensurePluginContainer(VelocityVirtualPlugin.INSTANCE).getExecutorService();
|
||||
} else {
|
||||
// it's some other thread that isn't a Netty event loop thread. direct execution it is!
|
||||
return MoreExecutors.directExecutor();
|
||||
}
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.context.ParsedArgument;
|
||||
import com.mojang.brigadier.context.ParsedCommandNode;
|
||||
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import com.mojang.brigadier.tree.RootCommandNode;
|
||||
@ -32,6 +33,7 @@ import com.velocitypowered.api.command.CommandManager;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.command.InvocableCommand;
|
||||
import com.velocitypowered.proxy.command.brigadier.VelocityArgumentCommandNode;
|
||||
import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@ -44,6 +46,59 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
*/
|
||||
public final class VelocityCommands {
|
||||
|
||||
// Wrapping
|
||||
|
||||
/**
|
||||
* Walks the command node tree and wraps all {@link Command} instances in a {@link VelocityBrigadierCommandWrapper},
|
||||
* to indicate the plugin that registered the command. This also has the side effect of cloning
|
||||
* the command node tree.
|
||||
*
|
||||
* @param delegate the command node to wrap
|
||||
* @param registrant the plugin that registered the command
|
||||
* @return the wrapped command node
|
||||
*/
|
||||
public static CommandNode<CommandSource> wrap(final CommandNode<CommandSource> delegate,
|
||||
final @Nullable Object registrant) {
|
||||
Preconditions.checkNotNull(delegate, "delegate");
|
||||
if (registrant == null) {
|
||||
// the registrant is null if the `plugin` was absent when we try to register the command
|
||||
return delegate;
|
||||
}
|
||||
|
||||
com.mojang.brigadier.Command<CommandSource> maybeCommand = delegate.getCommand();
|
||||
if (maybeCommand != null && !(maybeCommand instanceof VelocityBrigadierCommandWrapper)) {
|
||||
maybeCommand = VelocityBrigadierCommandWrapper.wrap(delegate.getCommand(), registrant);
|
||||
}
|
||||
|
||||
if (delegate instanceof LiteralCommandNode<CommandSource> lcn) {
|
||||
var literalBuilder = shallowCopyAsBuilder(lcn, delegate.getName(), true);
|
||||
literalBuilder.executes(maybeCommand);
|
||||
// we also need to wrap any children
|
||||
for (final CommandNode<CommandSource> child : delegate.getChildren()) {
|
||||
literalBuilder.then(wrap(child, registrant));
|
||||
}
|
||||
if (delegate.getRedirect() != null) {
|
||||
literalBuilder.redirect(wrap(delegate.getRedirect(), registrant));
|
||||
}
|
||||
return literalBuilder.build();
|
||||
} else if (delegate instanceof VelocityArgumentCommandNode<CommandSource, ?> vacn) {
|
||||
return vacn.withCommand(maybeCommand)
|
||||
.withRedirect(delegate.getRedirect() != null ? wrap(delegate.getRedirect(), registrant) : null);
|
||||
} else if (delegate instanceof ArgumentCommandNode) {
|
||||
var argBuilder = delegate.createBuilder().executes(maybeCommand);
|
||||
// we also need to wrap any children
|
||||
for (final CommandNode<CommandSource> child : delegate.getChildren()) {
|
||||
argBuilder.then(wrap(child, registrant));
|
||||
}
|
||||
if (delegate.getRedirect() != null) {
|
||||
argBuilder.redirect(wrap(delegate.getRedirect(), registrant));
|
||||
}
|
||||
return argBuilder.build();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported node type: " + delegate.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
// Normalization
|
||||
|
||||
/**
|
||||
@ -135,6 +190,33 @@ public final class VelocityCommands {
|
||||
*/
|
||||
public static LiteralCommandNode<CommandSource> shallowCopy(
|
||||
final LiteralCommandNode<CommandSource> original, final String newName) {
|
||||
return shallowCopy(original, newName, original.getCommand());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the given literal with the specified name.
|
||||
*
|
||||
* @param original the literal node to copy
|
||||
* @param newName the name of the returned literal node
|
||||
* @param newCommand the new command to set on the copied node
|
||||
* @return a copy of the literal with the given name
|
||||
*/
|
||||
private static LiteralCommandNode<CommandSource> shallowCopy(
|
||||
final LiteralCommandNode<CommandSource> original, final String newName,
|
||||
final com.mojang.brigadier.Command<CommandSource> newCommand) {
|
||||
return shallowCopyAsBuilder(original, newName, false).executes(newCommand).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the given literal with the specified name.
|
||||
*
|
||||
* @param original the literal node to copy
|
||||
* @param newName the name of the returned literal node
|
||||
* @return a copy of the literal with the given name
|
||||
*/
|
||||
private static LiteralArgumentBuilder<CommandSource> shallowCopyAsBuilder(
|
||||
final LiteralCommandNode<CommandSource> original, final String newName,
|
||||
final boolean skipChildren) {
|
||||
// Brigadier resolves the redirect of a node if further input can be parsed.
|
||||
// Let <bar> be a literal node having a redirect to a <foo> literal. Then,
|
||||
// the context returned by CommandDispatcher#parseNodes when given the input
|
||||
@ -150,10 +232,12 @@ public final class VelocityCommands {
|
||||
.requiresWithContext(original.getContextRequirement())
|
||||
.forward(original.getRedirect(), original.getRedirectModifier(), original.isFork())
|
||||
.executes(original.getCommand());
|
||||
for (final CommandNode<CommandSource> child : original.getChildren()) {
|
||||
builder.then(child);
|
||||
if (!skipChildren) {
|
||||
for (final CommandNode<CommandSource> child : original.getChildren()) {
|
||||
builder.then(child);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
return builder;
|
||||
}
|
||||
|
||||
// Arguments node
|
||||
|
@ -93,6 +93,16 @@ public class VelocityArgumentCommandNode<S, T> extends ArgumentCommandNode<S, St
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public VelocityArgumentCommandNode<S, T> withCommand(Command<S> command) {
|
||||
return new VelocityArgumentCommandNode<>(getName(), type, command, getRequirement(),
|
||||
getContextRequirement(), getRedirect(), getRedirectModifier(), isFork(), getCustomSuggestions());
|
||||
}
|
||||
|
||||
public VelocityArgumentCommandNode<S, T> withRedirect(CommandNode<S> target) {
|
||||
return new VelocityArgumentCommandNode<>(getName(), type, getCommand(), getRequirement(),
|
||||
getContextRequirement(), target, getRedirectModifier(), isFork(), getCustomSuggestions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidInput(final String input) {
|
||||
return true;
|
||||
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Velocity 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.velocitypowered.proxy.command.brigadier;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Wraps a Brigadier command to allow us to track the registrant.
|
||||
*/
|
||||
public class VelocityBrigadierCommandWrapper implements Command<CommandSource> {
|
||||
|
||||
private final Command<CommandSource> delegate;
|
||||
private final Object registrant;
|
||||
|
||||
private VelocityBrigadierCommandWrapper(Command<CommandSource> delegate, Object registrant) {
|
||||
this.delegate = delegate;
|
||||
this.registrant = registrant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the given command into a {@code VelocityBrigadierCommandWrapper} if the registrant
|
||||
* is not null and if the command is not already wrapped.
|
||||
*
|
||||
* @param delegate the command to wrap
|
||||
* @param registrant the registrant of the command
|
||||
* @return the wrapped command, if necessary
|
||||
*/
|
||||
public static Command<CommandSource> wrap(Command<CommandSource> delegate, @Nullable Object registrant) {
|
||||
if (registrant == null) {
|
||||
// nothing to wrap
|
||||
return delegate;
|
||||
}
|
||||
if (delegate instanceof VelocityBrigadierCommandWrapper) {
|
||||
// already wrapped
|
||||
return delegate;
|
||||
}
|
||||
return new VelocityBrigadierCommandWrapper(delegate, registrant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int run(CommandContext<CommandSource> context) throws CommandSyntaxException {
|
||||
return delegate.run(context);
|
||||
}
|
||||
|
||||
public Object registrant() {
|
||||
return registrant;
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import com.velocitypowered.api.permission.Tristate;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@ -80,7 +81,13 @@ public class GlistCommand {
|
||||
.executes(this::serverCount)
|
||||
.build();
|
||||
rootNode.then(serverNode);
|
||||
server.getCommandManager().register(new BrigadierCommand(rootNode));
|
||||
final BrigadierCommand command = new BrigadierCommand(rootNode);
|
||||
server.getCommandManager().register(
|
||||
server.getCommandManager().metaBuilder(command)
|
||||
.plugin(VelocityVirtualPlugin.INSTANCE)
|
||||
.build(),
|
||||
command
|
||||
);
|
||||
}
|
||||
|
||||
private int totalCount(final CommandContext<CommandSource> context) {
|
||||
|
@ -30,6 +30,7 @@ import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@ -96,7 +97,13 @@ public class SendCommand {
|
||||
.build();
|
||||
playerNode.then(serverNode);
|
||||
rootNode.then(playerNode.build());
|
||||
server.getCommandManager().register(new BrigadierCommand(rootNode.build()));
|
||||
final BrigadierCommand command = new BrigadierCommand(rootNode);
|
||||
server.getCommandManager().register(
|
||||
server.getCommandManager().metaBuilder(command)
|
||||
.plugin(VelocityVirtualPlugin.INSTANCE)
|
||||
.build(),
|
||||
command
|
||||
);
|
||||
}
|
||||
|
||||
private int usage(final CommandContext<CommandSource> context) {
|
||||
|
@ -40,17 +40,19 @@ public final class BrigadierCommandRegistrar extends AbstractCommandRegistrar<Br
|
||||
// Register it (if valid), since it's probably what the user expects.
|
||||
// If invalid, the metadata contains the same alias, but in lowercase.
|
||||
final LiteralCommandNode<CommandSource> literal = command.getNode();
|
||||
final LiteralCommandNode<CommandSource> wrapped =
|
||||
(LiteralCommandNode<CommandSource>) VelocityCommands.wrap(literal, meta.getPlugin());
|
||||
final String primaryAlias = literal.getName();
|
||||
if (VelocityCommands.isValidAlias(primaryAlias)) {
|
||||
// Register directly without copying
|
||||
this.register(literal);
|
||||
this.register(wrapped);
|
||||
}
|
||||
|
||||
for (final String alias : meta.getAliases()) {
|
||||
if (primaryAlias.equals(alias)) {
|
||||
continue;
|
||||
}
|
||||
this.register(literal, alias);
|
||||
this.register(wrapped, alias);
|
||||
}
|
||||
|
||||
// Brigadier commands don't support hinting, ignore
|
||||
|
@ -32,6 +32,7 @@ import com.velocitypowered.api.command.InvocableCommand;
|
||||
import com.velocitypowered.proxy.command.VelocityCommandMeta;
|
||||
import com.velocitypowered.proxy.command.VelocityCommands;
|
||||
import com.velocitypowered.proxy.command.brigadier.VelocityArgumentBuilder;
|
||||
import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper;
|
||||
import com.velocitypowered.proxy.command.invocation.CommandInvocationFactory;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
@ -76,11 +77,11 @@ abstract class InvocableCommandRegistrar<T extends InvocableCommand<I>,
|
||||
final I invocation = invocationFactory.create(context);
|
||||
return command.hasPermission(invocation);
|
||||
};
|
||||
final Command<CommandSource> callback = context -> {
|
||||
final Command<CommandSource> callback = VelocityBrigadierCommandWrapper.wrap(context -> {
|
||||
final I invocation = invocationFactory.create(context);
|
||||
command.execute(invocation);
|
||||
return 1; // handled
|
||||
};
|
||||
}, meta.getPlugin());
|
||||
|
||||
final LiteralCommandNode<CommandSource> literal = LiteralArgumentBuilder
|
||||
.<CommandSource>literal(alias)
|
||||
|
@ -68,7 +68,12 @@ public class VelocityPluginManager implements PluginManager {
|
||||
this.server = checkNotNull(server, "server");
|
||||
}
|
||||
|
||||
private void registerPlugin(PluginContainer plugin) {
|
||||
/**
|
||||
* Registers a plugin with the plugin manager.
|
||||
*
|
||||
* @param plugin the plugin to register
|
||||
*/
|
||||
public void registerPlugin(PluginContainer plugin) {
|
||||
pluginsById.put(plugin.getDescription().getId(), plugin);
|
||||
Optional<?> instance = plugin.getInstance();
|
||||
instance.ifPresent(o -> pluginInstances.put(o, plugin));
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Velocity 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.velocitypowered.proxy.plugin.virtual;
|
||||
|
||||
/**
|
||||
* A singleton plugin object that represents the Velocity proxy itself.
|
||||
*/
|
||||
public class VelocityVirtualPlugin {
|
||||
@SuppressWarnings("InstantiationOfUtilityClass")
|
||||
public static final VelocityVirtualPlugin INSTANCE = new VelocityVirtualPlugin();
|
||||
|
||||
private VelocityVirtualPlugin() {
|
||||
}
|
||||
}
|
@ -230,7 +230,7 @@ public class BrigadierCommandTests extends CommandTestSuite {
|
||||
final Exception wrapper = assertThrows(CompletionException.class, () ->
|
||||
manager.executeAsync(source, "hello").join());
|
||||
|
||||
assertSame(expected, wrapper.getCause().getCause());
|
||||
assertSame(expected, wrapper.getCause());
|
||||
}
|
||||
|
||||
// Suggestions
|
||||
|
@ -29,6 +29,7 @@ import com.velocitypowered.api.permission.Tristate;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.proxy.event.MockEventManager;
|
||||
import com.velocitypowered.proxy.event.VelocityEventManager;
|
||||
import com.velocitypowered.proxy.testutil.FakePluginManager;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@ -48,7 +49,7 @@ abstract class CommandTestSuite {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.manager = new VelocityCommandManager(eventManager);
|
||||
this.manager = new VelocityCommandManager(eventManager, new FakePluginManager());
|
||||
}
|
||||
|
||||
final void assertHandled(final String input) {
|
||||
|
@ -22,6 +22,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.velocitypowered.api.plugin.PluginContainer;
|
||||
import com.velocitypowered.api.plugin.PluginDescription;
|
||||
import com.velocitypowered.api.plugin.PluginManager;
|
||||
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
@ -39,6 +40,8 @@ public class FakePluginManager implements PluginManager {
|
||||
|
||||
private final PluginContainer containerA = new FakePluginContainer("a", PLUGIN_A);
|
||||
private final PluginContainer containerB = new FakePluginContainer("b", PLUGIN_B);
|
||||
private final PluginContainer containerVelocity = new FakePluginContainer("velocity",
|
||||
VelocityVirtualPlugin.INSTANCE);
|
||||
|
||||
private ExecutorService service = Executors.newCachedThreadPool(
|
||||
new ThreadFactoryBuilder().setNameFormat("Test Async Thread").setDaemon(true).build()
|
||||
@ -50,6 +53,8 @@ public class FakePluginManager implements PluginManager {
|
||||
return Optional.of(containerA);
|
||||
} else if (instance == PLUGIN_B) {
|
||||
return Optional.of(containerB);
|
||||
} else if (instance == VelocityVirtualPlugin.INSTANCE) {
|
||||
return Optional.of(containerVelocity);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@ -62,6 +67,8 @@ public class FakePluginManager implements PluginManager {
|
||||
return Optional.of(containerA);
|
||||
case "b":
|
||||
return Optional.of(containerB);
|
||||
case "velocity":
|
||||
return Optional.of(containerVelocity);
|
||||
default:
|
||||
return Optional.empty();
|
||||
}
|
||||
@ -69,7 +76,7 @@ public class FakePluginManager implements PluginManager {
|
||||
|
||||
@Override
|
||||
public @NonNull Collection<PluginContainer> getPlugins() {
|
||||
return ImmutableList.of(containerA, containerB);
|
||||
return ImmutableList.of(containerVelocity, containerA, containerB);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren