3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-09-29 06:30:16 +02:00

Deprecate anonymous command registrations

Dieser Commit ist enthalten in:
Andrew Steinborn 2024-09-15 20:22:55 -04:00
Ursprung 4f227badc2
Commit 2016d1482f
15 geänderte Dateien mit 338 neuen und 37 gelöschten Zeilen

Datei anzeigen

@ -45,7 +45,9 @@ public interface CommandManager {
* @throws IllegalArgumentException if one of the given aliases is already registered, or * @throws IllegalArgumentException if one of the given aliases is already registered, or
* the given command does not implement a registrable {@link Command} subinterface * the given command does not implement a registrable {@link Command} subinterface
* @see Command for a list of registrable Command subinterfaces * @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) { default void register(String alias, Command command, String... otherAliases) {
register(metaBuilder(alias).aliases(otherAliases).build(), command); register(metaBuilder(alias).aliases(otherAliases).build(), command);
} }
@ -55,7 +57,9 @@ public interface CommandManager {
* *
* @param command the command to register * @param command the command to register
* @throws IllegalArgumentException if the node alias is already registered * @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); void register(BrigadierCommand command);
/** /**

Datei anzeigen

@ -22,11 +22,13 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.velocitypowered.api.command.BrigadierCommand;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyReloadEvent; import com.velocitypowered.api.event.proxy.ProxyReloadEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer; 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.event.VelocityEventManager;
import com.velocitypowered.proxy.network.ConnectionManager; import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.plugin.VelocityPluginManager; 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.ProtocolUtils;
import com.velocitypowered.proxy.protocol.util.FaviconSerializer; import com.velocitypowered.proxy.protocol.util.FaviconSerializer;
import com.velocitypowered.proxy.protocol.util.GameProfileSerializer; import com.velocitypowered.proxy.protocol.util.GameProfileSerializer;
@ -77,6 +82,7 @@ import java.nio.file.Path;
import java.security.KeyPair; import java.security.KeyPair;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -111,6 +117,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*/ */
public class VelocityServer implements ProxyServer, ForwardingAudience { public class VelocityServer implements ProxyServer, ForwardingAudience {
public static final String VELOCITY_URL = "https://velocitypowered.com";
private static final Logger logger = LogManager.getLogger(VelocityServer.class); private static final Logger logger = LogManager.getLogger(VelocityServer.class);
public static final Gson GENERAL_GSON = new GsonBuilder() public static final Gson GENERAL_GSON = new GsonBuilder()
.registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE)
@ -163,7 +171,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
VelocityServer(final ProxyOptions options) { VelocityServer(final ProxyOptions options) {
pluginManager = new VelocityPluginManager(this); pluginManager = new VelocityPluginManager(this);
eventManager = new VelocityEventManager(pluginManager); eventManager = new VelocityEventManager(pluginManager);
commandManager = new VelocityCommandManager(eventManager); commandManager = new VelocityCommandManager(eventManager, pluginManager);
scheduler = new VelocityScheduler(pluginManager); scheduler = new VelocityScheduler(pluginManager);
console = new VelocityConsole(this); console = new VelocityConsole(this);
cm = new ConnectionManager(this); cm = new ConnectionManager(this);
@ -200,6 +208,16 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
return new ProxyVersion(implName, implVendor, implVersion); 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 @Override
public VelocityCommandManager getCommandManager() { public VelocityCommandManager getCommandManager() {
return commandManager; return commandManager;
@ -214,6 +232,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
void start() { void start() {
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion()); logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
console.setupStreams(); console.setupStreams();
pluginManager.registerPlugin(this.createVirtualPlugin());
registerTranslations(); registerTranslations();
@ -222,11 +241,35 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
cm.logChannelInformation(); cm.logChannelInformation();
// Initialize commands first // Initialize commands first
commandManager.register(VelocityCommand.create(this)); final BrigadierCommand velocityParentCommand = VelocityCommand.create(this);
commandManager.register(CallbackCommand.create()); commandManager.register(
commandManager.register(ServerCommand.create(this)); commandManager.metaBuilder(velocityParentCommand)
commandManager.register("shutdown", ShutdownCommand.command(this), .plugin(VelocityVirtualPlugin.INSTANCE)
"end", "stop"); .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 GlistCommand(this).register();
new SendCommand(this).register(); new SendCommand(this).register();

Datei anzeigen

@ -20,6 +20,7 @@ package com.velocitypowered.proxy.command;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.util.concurrent.MoreExecutors;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.Message; import com.mojang.brigadier.Message;
import com.mojang.brigadier.ParseResults; 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.command.VelocityBrigadierMessage;
import com.velocitypowered.api.event.command.CommandExecuteEvent; import com.velocitypowered.api.event.command.CommandExecuteEvent;
import com.velocitypowered.api.event.command.PostCommandInvocationEvent; 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.BrigadierCommandRegistrar;
import com.velocitypowered.proxy.command.registrar.CommandRegistrar; import com.velocitypowered.proxy.command.registrar.CommandRegistrar;
import com.velocitypowered.proxy.command.registrar.RawCommandRegistrar; import com.velocitypowered.proxy.command.registrar.RawCommandRegistrar;
import com.velocitypowered.proxy.command.registrar.SimpleCommandRegistrar; import com.velocitypowered.proxy.command.registrar.SimpleCommandRegistrar;
import com.velocitypowered.proxy.event.VelocityEventManager; 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.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -49,8 +54,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -73,14 +77,16 @@ public class VelocityCommandManager implements CommandManager {
private final SuggestionsProvider<CommandSource> suggestionsProvider; private final SuggestionsProvider<CommandSource> suggestionsProvider;
private final CommandGraphInjector<CommandSource> injector; private final CommandGraphInjector<CommandSource> injector;
private final Map<String, CommandMeta> commandMetas; private final Map<String, CommandMeta> commandMetas;
private final ExecutorService asyncExecutor; private final PluginManager pluginManager;
/** /**
* Constructs a command manager. * Constructs a command manager.
* *
* @param eventManager the event 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.lock = new ReentrantReadWriteLock();
this.dispatcher = new CommandDispatcher<>(); this.dispatcher = new CommandDispatcher<>();
this.eventManager = Preconditions.checkNotNull(eventManager); this.eventManager = Preconditions.checkNotNull(eventManager);
@ -92,7 +98,6 @@ public class VelocityCommandManager implements CommandManager {
this.suggestionsProvider = new SuggestionsProvider<>(this.dispatcher, this.lock.readLock()); this.suggestionsProvider = new SuggestionsProvider<>(this.dispatcher, this.lock.readLock());
this.injector = new CommandGraphInjector<>(this.dispatcher, this.lock.readLock()); this.injector = new CommandGraphInjector<>(this.dispatcher, this.lock.readLock());
this.commandMetas = new ConcurrentHashMap<>(); this.commandMetas = new ConcurrentHashMap<>();
this.asyncExecutor = ForkJoinPool.commonPool(); // TODO: remove entirely
} }
public void setAnnounceProxyCommands(boolean announceProxyCommands) { public void setAnnounceProxyCommands(boolean announceProxyCommands) {
@ -222,16 +227,13 @@ public class VelocityCommandManager implements CommandManager {
return eventManager.fire(new CommandExecuteEvent(source, cmdLine)); 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(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine");
final String normalizedInput = VelocityCommands.normalizeInput(cmdLine, true);
CommandResult result = CommandResult.EXCEPTION; CommandResult result = CommandResult.EXCEPTION;
try { try {
// The parse can fail if the requirement predicates throw // The parse can fail if the requirement predicates throw
final ParseResults<CommandSource> parse = this.parse(normalizedInput, source); boolean executed = dispatcher.execute(parsed) != BrigadierCommand.FORWARD;
boolean executed = dispatcher.execute(parse) != BrigadierCommand.FORWARD;
result = executed ? CommandResult.EXECUTED : CommandResult.FORWARDED; result = executed ? CommandResult.EXECUTED : CommandResult.FORWARDED;
return executed; return executed;
} catch (final CommandSyntaxException e) { } catch (final CommandSyntaxException e) {
@ -253,9 +255,9 @@ public class VelocityCommandManager implements CommandManager {
} }
} catch (final Throwable e) { } catch (final Throwable e) {
// Ugly, ugly swallowing of everything Throwable, because plugins are naughty. // 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 { } 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(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine"); Preconditions.checkNotNull(cmdLine, "cmdLine");
return callCommandEvent(source, cmdLine).thenApplyAsync(event -> { return callCommandEvent(source, cmdLine).thenComposeAsync(event -> {
CommandExecuteEvent.CommandResult commandResult = event.getResult(); CommandExecuteEvent.CommandResult commandResult = event.getResult();
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) { if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
return false; return CompletableFuture.completedFuture(false);
} }
return executeImmediately0(source, commandResult.getCommand().orElse(event.getCommand())); final ParseResults<CommandSource> parsed = this.parse(
}, asyncExecutor); commandResult.getCommand().orElse(cmdLine), source);
return CompletableFuture.supplyAsync(
() -> executeImmediately0(source, parsed), this.getAsyncExecutor(parsed)
);
}, figureAsyncExecutorForParsing());
} }
@Override @Override
@ -279,7 +285,13 @@ public class VelocityCommandManager implements CommandManager {
Preconditions.checkNotNull(source, "source"); Preconditions.checkNotNull(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine"); 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 * @return the parse results
*/ */
private ParseResults<CommandSource> parse(final String input, final CommandSource source) { private ParseResults<CommandSource> parse(final String input, final CommandSource source) {
final String normalizedInput = VelocityCommands.normalizeInput(input, true);
lock.readLock().lock(); lock.readLock().lock();
try { try {
return dispatcher.parse(input, source); return dispatcher.parse(normalizedInput, source);
} finally { } finally {
lock.readLock().unlock(); lock.readLock().unlock();
} }
@ -373,4 +386,25 @@ public class VelocityCommandManager implements CommandManager {
public CommandGraphInjector<CommandSource> getInjector() { public CommandGraphInjector<CommandSource> getInjector() {
return injector; 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();
}
}
} }

Datei anzeigen

@ -24,6 +24,7 @@ import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.context.ParsedArgument; import com.mojang.brigadier.context.ParsedArgument;
import com.mojang.brigadier.context.ParsedCommandNode; import com.mojang.brigadier.context.ParsedCommandNode;
import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode; 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.CommandSource;
import com.velocitypowered.api.command.InvocableCommand; import com.velocitypowered.api.command.InvocableCommand;
import com.velocitypowered.proxy.command.brigadier.VelocityArgumentCommandNode; import com.velocitypowered.proxy.command.brigadier.VelocityArgumentCommandNode;
import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -44,6 +46,59 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*/ */
public final class VelocityCommands { 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 // Normalization
/** /**
@ -135,6 +190,33 @@ public final class VelocityCommands {
*/ */
public static LiteralCommandNode<CommandSource> shallowCopy( public static LiteralCommandNode<CommandSource> shallowCopy(
final LiteralCommandNode<CommandSource> original, final String newName) { 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. // 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, // Let <bar> be a literal node having a redirect to a <foo> literal. Then,
// the context returned by CommandDispatcher#parseNodes when given the input // the context returned by CommandDispatcher#parseNodes when given the input
@ -150,10 +232,12 @@ public final class VelocityCommands {
.requiresWithContext(original.getContextRequirement()) .requiresWithContext(original.getContextRequirement())
.forward(original.getRedirect(), original.getRedirectModifier(), original.isFork()) .forward(original.getRedirect(), original.getRedirectModifier(), original.isFork())
.executes(original.getCommand()); .executes(original.getCommand());
for (final CommandNode<CommandSource> child : original.getChildren()) { if (!skipChildren) {
builder.then(child); for (final CommandNode<CommandSource> child : original.getChildren()) {
builder.then(child);
}
} }
return builder.build(); return builder;
} }
// Arguments node // Arguments node

Datei anzeigen

@ -93,6 +93,16 @@ public class VelocityArgumentCommandNode<S, T> extends ArgumentCommandNode<S, St
throw new UnsupportedOperationException(); 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 @Override
public boolean isValidInput(final String input) { public boolean isValidInput(final String input) {
return true; return true;

Datei anzeigen

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

Datei anzeigen

@ -31,6 +31,7 @@ import com.velocitypowered.api.permission.Tristate;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@ -80,7 +81,13 @@ public class GlistCommand {
.executes(this::serverCount) .executes(this::serverCount)
.build(); .build();
rootNode.then(serverNode); 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) { private int totalCount(final CommandContext<CommandSource> context) {

Datei anzeigen

@ -30,6 +30,7 @@ import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@ -96,7 +97,13 @@ public class SendCommand {
.build(); .build();
playerNode.then(serverNode); playerNode.then(serverNode);
rootNode.then(playerNode.build()); 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) { private int usage(final CommandContext<CommandSource> context) {

Datei anzeigen

@ -40,17 +40,19 @@ public final class BrigadierCommandRegistrar extends AbstractCommandRegistrar<Br
// Register it (if valid), since it's probably what the user expects. // Register it (if valid), since it's probably what the user expects.
// If invalid, the metadata contains the same alias, but in lowercase. // If invalid, the metadata contains the same alias, but in lowercase.
final LiteralCommandNode<CommandSource> literal = command.getNode(); final LiteralCommandNode<CommandSource> literal = command.getNode();
final LiteralCommandNode<CommandSource> wrapped =
(LiteralCommandNode<CommandSource>) VelocityCommands.wrap(literal, meta.getPlugin());
final String primaryAlias = literal.getName(); final String primaryAlias = literal.getName();
if (VelocityCommands.isValidAlias(primaryAlias)) { if (VelocityCommands.isValidAlias(primaryAlias)) {
// Register directly without copying // Register directly without copying
this.register(literal); this.register(wrapped);
} }
for (final String alias : meta.getAliases()) { for (final String alias : meta.getAliases()) {
if (primaryAlias.equals(alias)) { if (primaryAlias.equals(alias)) {
continue; continue;
} }
this.register(literal, alias); this.register(wrapped, alias);
} }
// Brigadier commands don't support hinting, ignore // Brigadier commands don't support hinting, ignore

Datei anzeigen

@ -32,6 +32,7 @@ import com.velocitypowered.api.command.InvocableCommand;
import com.velocitypowered.proxy.command.VelocityCommandMeta; import com.velocitypowered.proxy.command.VelocityCommandMeta;
import com.velocitypowered.proxy.command.VelocityCommands; import com.velocitypowered.proxy.command.VelocityCommands;
import com.velocitypowered.proxy.command.brigadier.VelocityArgumentBuilder; import com.velocitypowered.proxy.command.brigadier.VelocityArgumentBuilder;
import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper;
import com.velocitypowered.proxy.command.invocation.CommandInvocationFactory; import com.velocitypowered.proxy.command.invocation.CommandInvocationFactory;
import java.util.Iterator; import java.util.Iterator;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
@ -76,11 +77,11 @@ abstract class InvocableCommandRegistrar<T extends InvocableCommand<I>,
final I invocation = invocationFactory.create(context); final I invocation = invocationFactory.create(context);
return command.hasPermission(invocation); return command.hasPermission(invocation);
}; };
final Command<CommandSource> callback = context -> { final Command<CommandSource> callback = VelocityBrigadierCommandWrapper.wrap(context -> {
final I invocation = invocationFactory.create(context); final I invocation = invocationFactory.create(context);
command.execute(invocation); command.execute(invocation);
return 1; // handled return 1; // handled
}; }, meta.getPlugin());
final LiteralCommandNode<CommandSource> literal = LiteralArgumentBuilder final LiteralCommandNode<CommandSource> literal = LiteralArgumentBuilder
.<CommandSource>literal(alias) .<CommandSource>literal(alias)

Datei anzeigen

@ -68,7 +68,12 @@ public class VelocityPluginManager implements PluginManager {
this.server = checkNotNull(server, "server"); 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); pluginsById.put(plugin.getDescription().getId(), plugin);
Optional<?> instance = plugin.getInstance(); Optional<?> instance = plugin.getInstance();
instance.ifPresent(o -> pluginInstances.put(o, plugin)); instance.ifPresent(o -> pluginInstances.put(o, plugin));

Datei anzeigen

@ -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() {
}
}

Datei anzeigen

@ -230,7 +230,7 @@ public class BrigadierCommandTests extends CommandTestSuite {
final Exception wrapper = assertThrows(CompletionException.class, () -> final Exception wrapper = assertThrows(CompletionException.class, () ->
manager.executeAsync(source, "hello").join()); manager.executeAsync(source, "hello").join());
assertSame(expected, wrapper.getCause().getCause()); assertSame(expected, wrapper.getCause());
} }
// Suggestions // Suggestions

Datei anzeigen

@ -29,6 +29,7 @@ import com.velocitypowered.api.permission.Tristate;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.proxy.event.MockEventManager; import com.velocitypowered.proxy.event.MockEventManager;
import com.velocitypowered.proxy.event.VelocityEventManager; import com.velocitypowered.proxy.event.VelocityEventManager;
import com.velocitypowered.proxy.testutil.FakePluginManager;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
@ -48,7 +49,7 @@ abstract class CommandTestSuite {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
this.manager = new VelocityCommandManager(eventManager); this.manager = new VelocityCommandManager(eventManager, new FakePluginManager());
} }
final void assertHandled(final String input) { final void assertHandled(final String input) {

Datei anzeigen

@ -22,6 +22,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; 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 containerA = new FakePluginContainer("a", PLUGIN_A);
private final PluginContainer containerB = new FakePluginContainer("b", PLUGIN_B); private final PluginContainer containerB = new FakePluginContainer("b", PLUGIN_B);
private final PluginContainer containerVelocity = new FakePluginContainer("velocity",
VelocityVirtualPlugin.INSTANCE);
private ExecutorService service = Executors.newCachedThreadPool( private ExecutorService service = Executors.newCachedThreadPool(
new ThreadFactoryBuilder().setNameFormat("Test Async Thread").setDaemon(true).build() new ThreadFactoryBuilder().setNameFormat("Test Async Thread").setDaemon(true).build()
@ -50,6 +53,8 @@ public class FakePluginManager implements PluginManager {
return Optional.of(containerA); return Optional.of(containerA);
} else if (instance == PLUGIN_B) { } else if (instance == PLUGIN_B) {
return Optional.of(containerB); return Optional.of(containerB);
} else if (instance == VelocityVirtualPlugin.INSTANCE) {
return Optional.of(containerVelocity);
} else { } else {
return Optional.empty(); return Optional.empty();
} }
@ -62,6 +67,8 @@ public class FakePluginManager implements PluginManager {
return Optional.of(containerA); return Optional.of(containerA);
case "b": case "b":
return Optional.of(containerB); return Optional.of(containerB);
case "velocity":
return Optional.of(containerVelocity);
default: default:
return Optional.empty(); return Optional.empty();
} }
@ -69,7 +76,7 @@ public class FakePluginManager implements PluginManager {
@Override @Override
public @NonNull Collection<PluginContainer> getPlugins() { public @NonNull Collection<PluginContainer> getPlugins() {
return ImmutableList.of(containerA, containerB); return ImmutableList.of(containerVelocity, containerA, containerB);
} }
@Override @Override