fabric: Split out server-specific command manager to allow for client commands
This commit is contained in:
parent
6b690811f1
commit
48181164b0
5 changed files with 273 additions and 128 deletions
|
|
@ -37,6 +37,7 @@ import cloud.commandframework.meta.CommandMeta;
|
||||||
import cloud.commandframework.meta.SimpleCommandMeta;
|
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
import io.leangen.geantyref.TypeToken;
|
import io.leangen.geantyref.TypeToken;
|
||||||
|
import net.minecraft.command.CommandSource;
|
||||||
import net.minecraft.command.argument.AngleArgumentType;
|
import net.minecraft.command.argument.AngleArgumentType;
|
||||||
import net.minecraft.command.argument.BlockPredicateArgumentType;
|
import net.minecraft.command.argument.BlockPredicateArgumentType;
|
||||||
import net.minecraft.command.argument.ColorArgumentType;
|
import net.minecraft.command.argument.ColorArgumentType;
|
||||||
|
|
@ -61,48 +62,34 @@ import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.particle.ParticleEffect;
|
import net.minecraft.particle.ParticleEffect;
|
||||||
import net.minecraft.predicate.NumberRange;
|
import net.minecraft.predicate.NumberRange;
|
||||||
import net.minecraft.scoreboard.ScoreboardCriterion;
|
import net.minecraft.scoreboard.ScoreboardCriterion;
|
||||||
import net.minecraft.server.command.CommandManager.RegistrationEnvironment;
|
|
||||||
import net.minecraft.server.command.CommandOutput;
|
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
import net.minecraft.text.LiteralText;
|
|
||||||
import net.minecraft.util.Formatting;
|
import net.minecraft.util.Formatting;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.Vec2f;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class FabricCommandManager<C> extends CommandManager<C> implements BrigadierManagerHolder<C> {
|
/**
|
||||||
|
* A command manager for either the server or client on Fabric.
|
||||||
|
*
|
||||||
|
* <p>Commands registered with managers of this type will be registered into a Brigadier command tree.</p>
|
||||||
|
*
|
||||||
|
* <p>Where possible, Vanilla argument types are made available in a cloud-friendly format. In some cases, these argument
|
||||||
|
* types may only be available for server commands. Mod-provided argument types can be exposed to Cloud as well, by using
|
||||||
|
* {@link WrappedBrigadierParser}.</p>
|
||||||
|
*
|
||||||
|
* @param <C> the manager's sender type
|
||||||
|
* @param <S> the platform sender type
|
||||||
|
* @see FabricServerCommandManager for server commands
|
||||||
|
*/
|
||||||
|
public abstract class FabricCommandManager<C, S extends CommandSource> extends CommandManager<C> implements BrigadierManagerHolder<C> {
|
||||||
|
|
||||||
/**
|
private final Function<S, C> commandSourceMapper;
|
||||||
* A meta attribute specifying which environments a command should be registered in.
|
private final Function<C, S> backwardsCommandSourceMapper;
|
||||||
*
|
private final CloudBrigadierManager<C, S> brigadierManager;
|
||||||
* <p>The default value is {@link RegistrationEnvironment#ALL}.</p>
|
|
||||||
*/
|
|
||||||
public static final CommandMeta.Key<RegistrationEnvironment> META_REGISTRATION_ENVIRONMENT = CommandMeta.Key.of(
|
|
||||||
RegistrationEnvironment.class,
|
|
||||||
"cloud:registration-environment"
|
|
||||||
);
|
|
||||||
|
|
||||||
private final Function<ServerCommandSource, C> commandSourceMapper;
|
|
||||||
private final Function<C, ServerCommandSource> backwardsCommandSourceMapper;
|
|
||||||
private final CloudBrigadierManager<C, ServerCommandSource> brigadierManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a command manager using native source types.
|
|
||||||
*
|
|
||||||
* @param execCoordinator Execution coordinator instance.
|
|
||||||
* @return a new command manager
|
|
||||||
* @see #FabricCommandManager(Function, Function, Function) for a more thorough explanation
|
|
||||||
*/
|
|
||||||
public static FabricCommandManager<ServerCommandSource> createNative(
|
|
||||||
final Function<CommandTree<ServerCommandSource>, CommandExecutionCoordinator<ServerCommandSource>> execCoordinator
|
|
||||||
) {
|
|
||||||
return new FabricCommandManager<>(execCoordinator, Function.identity(), Function.identity());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -118,14 +105,18 @@ public class FabricCommandManager<C> extends CommandManager<C> implements Brigad
|
||||||
* {@link AsynchronousCommandExecutionCoordinator}
|
* {@link AsynchronousCommandExecutionCoordinator}
|
||||||
* @param commandSourceMapper Function that maps {@link ServerCommandSource} to the command sender type
|
* @param commandSourceMapper Function that maps {@link ServerCommandSource} to the command sender type
|
||||||
* @param backwardsCommandSourceMapper Function that maps the command sender type to {@link ServerCommandSource}
|
* @param backwardsCommandSourceMapper Function that maps the command sender type to {@link ServerCommandSource}
|
||||||
|
* @param registrationHandler the handler accepting command registrations
|
||||||
|
* @param dummyCommandSourceProvider a provider of a dummy command source, for use with brigadier registration
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected FabricCommandManager(
|
FabricCommandManager(
|
||||||
final @NonNull Function<@NonNull CommandTree<C>, @NonNull CommandExecutionCoordinator<C>> commandExecutionCoordinator,
|
final @NonNull Function<@NonNull CommandTree<C>, @NonNull CommandExecutionCoordinator<C>> commandExecutionCoordinator,
|
||||||
final Function<ServerCommandSource, C> commandSourceMapper,
|
final Function<S, C> commandSourceMapper,
|
||||||
final Function<C, ServerCommandSource> backwardsCommandSourceMapper
|
final Function<C, S> backwardsCommandSourceMapper,
|
||||||
) {
|
final FabricCommandRegistrationHandler<C, S> registrationHandler,
|
||||||
super(commandExecutionCoordinator, new FabricCommandRegistrationHandler<>());
|
final Supplier<S> dummyCommandSourceProvider
|
||||||
|
) {
|
||||||
|
super(commandExecutionCoordinator, registrationHandler);
|
||||||
this.commandSourceMapper = commandSourceMapper;
|
this.commandSourceMapper = commandSourceMapper;
|
||||||
this.backwardsCommandSourceMapper = backwardsCommandSourceMapper;
|
this.backwardsCommandSourceMapper = backwardsCommandSourceMapper;
|
||||||
|
|
||||||
|
|
@ -133,26 +124,16 @@ public class FabricCommandManager<C> extends CommandManager<C> implements Brigad
|
||||||
this.brigadierManager = new CloudBrigadierManager<>(this, () -> new CommandContext<>(
|
this.brigadierManager = new CloudBrigadierManager<>(this, () -> new CommandContext<>(
|
||||||
// This looks ugly, but it's what the server does when loading datapack functions in 1.16+
|
// This looks ugly, but it's what the server does when loading datapack functions in 1.16+
|
||||||
// See net.minecraft.server.function.FunctionLoader.reload for reference
|
// See net.minecraft.server.function.FunctionLoader.reload for reference
|
||||||
this.commandSourceMapper.apply(new ServerCommandSource(
|
this.commandSourceMapper.apply(dummyCommandSourceProvider.get()),
|
||||||
CommandOutput.DUMMY,
|
this
|
||||||
Vec3d.ZERO,
|
|
||||||
Vec2f.ZERO,
|
|
||||||
null,
|
|
||||||
4,
|
|
||||||
"",
|
|
||||||
LiteralText.EMPTY,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)),
|
|
||||||
this
|
|
||||||
));
|
));
|
||||||
this.brigadierManager.backwardsBrigadierSenderMapper(this.backwardsCommandSourceMapper);
|
this.brigadierManager.backwardsBrigadierSenderMapper(this.backwardsCommandSourceMapper);
|
||||||
this.registerNativeBrigadierMappings(this.brigadierManager);
|
this.registerNativeBrigadierMappings(this.brigadierManager);
|
||||||
|
|
||||||
((FabricCommandRegistrationHandler<C>) this.getCommandRegistrationHandler()).initialize(this);
|
((FabricCommandRegistrationHandler<C, S>) this.getCommandRegistrationHandler()).initialize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerNativeBrigadierMappings(final CloudBrigadierManager<C, ServerCommandSource> brigadier) {
|
private void registerNativeBrigadierMappings(final CloudBrigadierManager<C, S> brigadier) {
|
||||||
/* Cloud-native argument types */
|
/* Cloud-native argument types */
|
||||||
brigadier.registerMapping(new TypeToken<UUIDArgument.UUIDParser<C>>() {}, false, cloud -> UuidArgumentType.uuid());
|
brigadier.registerMapping(new TypeToken<UUIDArgument.UUIDParser<C>>() {}, false, cloud -> UuidArgumentType.uuid());
|
||||||
|
|
||||||
|
|
@ -200,27 +181,26 @@ public class FabricCommandManager<C> extends CommandManager<C> implements Brigad
|
||||||
// entity argument type: single or multiple, players or any entity -- returns EntitySelector, but do we want that?
|
// entity argument type: single or multiple, players or any entity -- returns EntitySelector, but do we want that?
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void registerConstantNativeParserSupplier(final Class<T> type, final ArgumentType<T> argument) {
|
/**
|
||||||
|
* Register a parser supplier for a brigadier type that has no options and whose output can be directly used.
|
||||||
|
*
|
||||||
|
* @param type the Java type to map
|
||||||
|
* @param argument the Brigadier parser
|
||||||
|
* @param <T> value type
|
||||||
|
*/
|
||||||
|
final <T> void registerConstantNativeParserSupplier(final Class<T> type, final ArgumentType<T> argument) {
|
||||||
this.registerConstantNativeParserSupplier(TypeToken.get(type), argument);
|
this.registerConstantNativeParserSupplier(TypeToken.get(type), argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void registerConstantNativeParserSupplier(final TypeToken<T> type, final ArgumentType<T> argument) {
|
|
||||||
this.getParserRegistry().registerParserSupplier(type, params -> new WrappedBrigadierParser<>(argument));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a sender has a certain permission.
|
* Register a parser supplier for a brigadier type that has no options and whose output can be directly used.
|
||||||
*
|
*
|
||||||
* <p>The current implementation checks op level, pending a full Fabric permissions api.</p>
|
* @param type the Java type to map
|
||||||
*
|
* @param argument the Brigadier parser
|
||||||
* @param sender Command sender
|
* @param <T> value type
|
||||||
* @param permission Permission node
|
|
||||||
* @return whether the sender has the specified permission
|
|
||||||
*/
|
*/
|
||||||
@Override
|
final <T> void registerConstantNativeParserSupplier(final TypeToken<T> type, final ArgumentType<T> argument) {
|
||||||
public boolean hasPermission(@NonNull final C sender, @NonNull final String permission) {
|
this.getParserRegistry().registerParserSupplier(type, params -> new WrappedBrigadierParser<>(argument));
|
||||||
final ServerCommandSource source = this.backwardsCommandSourceMapper.apply(sender);
|
|
||||||
return source.hasPermissionLevel(source.getMinecraftServer().getOpPermissionLevel());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -233,7 +213,7 @@ public class FabricCommandManager<C> extends CommandManager<C> implements Brigad
|
||||||
*
|
*
|
||||||
* @return Command source mapper
|
* @return Command source mapper
|
||||||
*/
|
*/
|
||||||
public final @NonNull Function<@NonNull ServerCommandSource, @NonNull C> getCommandSourceMapper() {
|
public final @NonNull Function<@NonNull S, @NonNull C> getCommandSourceMapper() {
|
||||||
return this.commandSourceMapper;
|
return this.commandSourceMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,12 +222,12 @@ public class FabricCommandManager<C> extends CommandManager<C> implements Brigad
|
||||||
*
|
*
|
||||||
* @return Command source mapper
|
* @return Command source mapper
|
||||||
*/
|
*/
|
||||||
public final @NonNull Function<@NonNull C, @NonNull ServerCommandSource> getBackwardsCommandSourceMapper() {
|
public final @NonNull Function<@NonNull C, @NonNull S> getBackwardsCommandSourceMapper() {
|
||||||
return this.backwardsCommandSourceMapper;
|
return this.backwardsCommandSourceMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final @NonNull CloudBrigadierManager<C, ServerCommandSource> brigadierManager() {
|
public final @NonNull CloudBrigadierManager<C, S> brigadierManager() {
|
||||||
return this.brigadierManager;
|
return this.brigadierManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import com.mojang.brigadier.CommandDispatcher;
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
import com.mojang.brigadier.tree.RootCommandNode;
|
import com.mojang.brigadier.tree.RootCommandNode;
|
||||||
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
|
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
|
||||||
|
import net.minecraft.command.CommandSource;
|
||||||
import net.minecraft.server.command.CommandManager;
|
import net.minecraft.server.command.CommandManager;
|
||||||
import net.minecraft.server.command.CommandManager.RegistrationEnvironment;
|
import net.minecraft.server.command.CommandManager.RegistrationEnvironment;
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
|
@ -43,57 +44,73 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
/**
|
/**
|
||||||
* A registration handler for Fabric API.
|
* A registration handler for Fabric API.
|
||||||
*
|
*
|
||||||
* <p>If the command registration callback has already been called, this will attempt
|
* <p>Subtypes exist for client and server commands.</p>
|
||||||
* to register with the active server's command dispatcher.</p>
|
|
||||||
*
|
*
|
||||||
* @param <C> command sender type
|
* @param <C> command sender type
|
||||||
|
* @param <S> native sender type
|
||||||
*/
|
*/
|
||||||
public final class FabricCommandRegistrationHandler<C> implements CommandRegistrationHandler {
|
abstract class FabricCommandRegistrationHandler<C, S extends CommandSource> implements CommandRegistrationHandler {
|
||||||
private @MonotonicNonNull FabricCommandManager<C> commandManager;
|
private @MonotonicNonNull FabricCommandManager<C, S> commandManager;
|
||||||
private final Set<Command<C>> registeredCommands = ConcurrentHashMap.newKeySet();
|
|
||||||
|
|
||||||
void initialize(final FabricCommandManager<C> manager) {
|
void initialize(final FabricCommandManager<C, S> manager) {
|
||||||
this.commandManager = manager;
|
this.commandManager = manager;
|
||||||
CommandRegistrationCallback.EVENT.register(this::registerAllCommands);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
FabricCommandManager<C, S> getCommandManager() {
|
||||||
@SuppressWarnings("unchecked")
|
return this.commandManager;
|
||||||
public boolean registerCommand(@NonNull final Command<?> command) {
|
|
||||||
return this.registeredCommands.add((Command<C>) command);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerAllCommands(final CommandDispatcher<ServerCommandSource> dispatcher, final boolean isDedicated) {
|
static class Server<C> extends FabricCommandRegistrationHandler<C, ServerCommandSource> {
|
||||||
this.commandManager.registrationCalled();
|
private final Set<Command<C>> registeredCommands = ConcurrentHashMap.newKeySet();
|
||||||
for (final Command<C> command : this.registeredCommands) {
|
|
||||||
/* Only register commands in the declared environment */
|
|
||||||
final RegistrationEnvironment env = command.getCommandMeta().getOrDefault(
|
|
||||||
FabricCommandManager.META_REGISTRATION_ENVIRONMENT,
|
|
||||||
RegistrationEnvironment.ALL
|
|
||||||
);
|
|
||||||
|
|
||||||
if ((env == RegistrationEnvironment.INTEGRATED && isDedicated)
|
@Override
|
||||||
|| (env == RegistrationEnvironment.DEDICATED && !isDedicated)) {
|
void initialize(final FabricCommandManager<C, ServerCommandSource> manager) {
|
||||||
continue;
|
super.initialize(manager);
|
||||||
}
|
CommandRegistrationCallback.EVENT.register(this::registerAllCommands);
|
||||||
this.registerCommand(dispatcher.getRoot(), command);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void registerCommand(final RootCommandNode<ServerCommandSource> dispatcher, final Command<C> command) {
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final StaticArgument<C> first = ((StaticArgument<C>) command.getArguments().get(0));
|
public boolean registerCommand(@NonNull final Command<?> command) {
|
||||||
final CommandNode<ServerCommandSource> baseNode = this.commandManager.brigadierManager().createLiteralCommandNode(
|
return this.registeredCommands.add((Command<C>) command);
|
||||||
first.getName(),
|
|
||||||
command,
|
|
||||||
(src, perm) -> this.commandManager.hasPermission(this.commandManager.getCommandSourceMapper().apply(src), perm),
|
|
||||||
true,
|
|
||||||
new FabricExecutor<>(this.commandManager));
|
|
||||||
|
|
||||||
dispatcher.addChild(baseNode);
|
|
||||||
|
|
||||||
for (final String alias : first.getAlternativeAliases()) {
|
|
||||||
dispatcher.addChild(CommandManager.literal(alias).redirect(baseNode).build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerAllCommands(final CommandDispatcher<ServerCommandSource> dispatcher, final boolean isDedicated) {
|
||||||
|
this.getCommandManager().registrationCalled();
|
||||||
|
for (final Command<C> command : this.registeredCommands) {
|
||||||
|
/* Only register commands in the declared environment */
|
||||||
|
final RegistrationEnvironment env = command.getCommandMeta().getOrDefault(
|
||||||
|
FabricServerCommandManager.META_REGISTRATION_ENVIRONMENT,
|
||||||
|
RegistrationEnvironment.ALL
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((env == RegistrationEnvironment.INTEGRATED && isDedicated)
|
||||||
|
|| (env == RegistrationEnvironment.DEDICATED && !isDedicated)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.registerCommand(dispatcher.getRoot(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerCommand(final RootCommandNode<ServerCommandSource> dispatcher, final Command<C> command) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final StaticArgument<C> first = ((StaticArgument<C>) command.getArguments().get(0));
|
||||||
|
final CommandNode<ServerCommandSource> baseNode = this.getCommandManager().brigadierManager().createLiteralCommandNode(
|
||||||
|
first.getName(),
|
||||||
|
command,
|
||||||
|
(src, perm) -> this.getCommandManager().hasPermission(
|
||||||
|
this.getCommandManager().getCommandSourceMapper().apply(src),
|
||||||
|
perm
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
new FabricExecutor<>(this.getCommandManager(), ServerCommandSource::getName, ServerCommandSource::sendError));
|
||||||
|
|
||||||
|
dispatcher.addChild(baseNode);
|
||||||
|
|
||||||
|
for (final String alias : first.getAlternativeAliases()) {
|
||||||
|
dispatcher.addChild(CommandManager.literal(alias).redirect(baseNode).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import cloud.commandframework.execution.CommandResult;
|
||||||
import com.mojang.brigadier.Command;
|
import com.mojang.brigadier.Command;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.command.CommandSource;
|
||||||
import net.minecraft.text.ClickEvent;
|
import net.minecraft.text.ClickEvent;
|
||||||
import net.minecraft.text.HoverEvent;
|
import net.minecraft.text.HoverEvent;
|
||||||
import net.minecraft.text.LiteralText;
|
import net.minecraft.text.LiteralText;
|
||||||
|
|
@ -50,8 +50,9 @@ import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
final class FabricExecutor<C, S extends CommandSource> implements Command<S> {
|
||||||
private static final Logger LOGGER = LogManager.getLogger();
|
private static final Logger LOGGER = LogManager.getLogger();
|
||||||
|
|
||||||
private static final Text NEWLINE = new LiteralText("\n");
|
private static final Text NEWLINE = new LiteralText("\n");
|
||||||
|
|
@ -61,15 +62,23 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
||||||
+ "Please contact the server administrators if you believe that this is in error.";
|
+ "Please contact the server administrators if you believe that this is in error.";
|
||||||
private static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command. Type \"/help\" for help.";
|
private static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command. Type \"/help\" for help.";
|
||||||
|
|
||||||
private final FabricCommandManager<C> manager;
|
private final FabricCommandManager<C, S> manager;
|
||||||
|
private final Function<S, String> getName;
|
||||||
|
private final BiConsumer<S, Text> sendError;
|
||||||
|
|
||||||
FabricExecutor(final @NonNull FabricCommandManager<C> manager) {
|
FabricExecutor(
|
||||||
|
final @NonNull FabricCommandManager<C, S> manager,
|
||||||
|
final @NonNull Function<S, String> getName,
|
||||||
|
final @NonNull BiConsumer<S, Text> sendError
|
||||||
|
) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.getName = getName;
|
||||||
|
this.sendError = sendError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int run(final @NonNull CommandContext<ServerCommandSource> ctx) {
|
public int run(final @NonNull CommandContext<S> ctx) {
|
||||||
final ServerCommandSource source = ctx.getSource();
|
final S source = ctx.getSource();
|
||||||
final String input = ctx.getInput().substring(1);
|
final String input = ctx.getInput().substring(1);
|
||||||
final C sender = this.manager.getCommandSourceMapper().apply(source);
|
final C sender = this.manager.getCommandSourceMapper().apply(source);
|
||||||
this.manager.executeCommand(sender, input).whenComplete(this.getResultConsumer(source, sender));
|
this.manager.executeCommand(sender, input).whenComplete(this.getResultConsumer(source, sender));
|
||||||
|
|
@ -77,7 +86,7 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull BiConsumer<@NonNull CommandResult<C>, ? super Throwable> getResultConsumer(
|
private @NonNull BiConsumer<@NonNull CommandResult<C>, ? super Throwable> getResultConsumer(
|
||||||
final @NonNull ServerCommandSource source,
|
final @NonNull S source,
|
||||||
final @NonNull C sender
|
final @NonNull C sender
|
||||||
) {
|
) {
|
||||||
return (result, throwable) -> {
|
return (result, throwable) -> {
|
||||||
|
|
@ -92,7 +101,8 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
||||||
InvalidSyntaxException.class,
|
InvalidSyntaxException.class,
|
||||||
(InvalidSyntaxException) throwable,
|
(InvalidSyntaxException) throwable,
|
||||||
(c, e) ->
|
(c, e) ->
|
||||||
source.sendError(
|
this.sendError.accept(
|
||||||
|
source,
|
||||||
new LiteralText("Invalid Command Syntax. Correct command syntax is: ")
|
new LiteralText("Invalid Command Syntax. Correct command syntax is: ")
|
||||||
.append(new LiteralText(e.getCorrectSyntax())
|
.append(new LiteralText(e.getCorrectSyntax())
|
||||||
.styled(style -> style.withColor(Formatting.GRAY))))
|
.styled(style -> style.withColor(Formatting.GRAY))))
|
||||||
|
|
@ -103,21 +113,21 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
||||||
InvalidCommandSenderException.class,
|
InvalidCommandSenderException.class,
|
||||||
(InvalidCommandSenderException) throwable,
|
(InvalidCommandSenderException) throwable,
|
||||||
(c, e) ->
|
(c, e) ->
|
||||||
source.sendError(new LiteralText(finalThrowable.getMessage()))
|
this.sendError.accept(source, new LiteralText(finalThrowable.getMessage()))
|
||||||
);
|
);
|
||||||
} else if (throwable instanceof NoPermissionException) {
|
} else if (throwable instanceof NoPermissionException) {
|
||||||
this.manager.handleException(
|
this.manager.handleException(
|
||||||
sender,
|
sender,
|
||||||
NoPermissionException.class,
|
NoPermissionException.class,
|
||||||
(NoPermissionException) throwable,
|
(NoPermissionException) throwable,
|
||||||
(c, e) -> source.sendError(new LiteralText(MESSAGE_NO_PERMS))
|
(c, e) -> this.sendError.accept(source, new LiteralText(MESSAGE_NO_PERMS))
|
||||||
);
|
);
|
||||||
} else if (throwable instanceof NoSuchCommandException) {
|
} else if (throwable instanceof NoSuchCommandException) {
|
||||||
this.manager.handleException(
|
this.manager.handleException(
|
||||||
sender,
|
sender,
|
||||||
NoSuchCommandException.class,
|
NoSuchCommandException.class,
|
||||||
(NoSuchCommandException) throwable,
|
(NoSuchCommandException) throwable,
|
||||||
(c, e) -> source.sendError(new LiteralText(MESSAGE_UNKNOWN_COMMAND))
|
(c, e) -> this.sendError.accept(source, new LiteralText(MESSAGE_UNKNOWN_COMMAND))
|
||||||
);
|
);
|
||||||
} else if (throwable instanceof ArgumentParseException) {
|
} else if (throwable instanceof ArgumentParseException) {
|
||||||
this.manager.handleException(
|
this.manager.handleException(
|
||||||
|
|
@ -126,12 +136,12 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
||||||
(ArgumentParseException) throwable,
|
(ArgumentParseException) throwable,
|
||||||
(c, e) -> {
|
(c, e) -> {
|
||||||
if (finalThrowable.getCause() instanceof CommandSyntaxException) {
|
if (finalThrowable.getCause() instanceof CommandSyntaxException) {
|
||||||
source.sendError(new LiteralText("Invalid Command Argument: ")
|
this.sendError.accept(source, new LiteralText("Invalid Command Argument: ")
|
||||||
.append(new LiteralText("")
|
.append(new LiteralText("")
|
||||||
.append(Texts.toText(((CommandSyntaxException) finalThrowable.getCause()).getRawMessage()))
|
.append(Texts.toText(((CommandSyntaxException) finalThrowable.getCause()).getRawMessage()))
|
||||||
.formatted(Formatting.GRAY)));
|
.formatted(Formatting.GRAY)));
|
||||||
} else {
|
} else {
|
||||||
source.sendError(new LiteralText("Invalid Command Argument: ")
|
this.sendError.accept(source, new LiteralText("Invalid Command Argument: ")
|
||||||
.append(new LiteralText(finalThrowable.getCause().getMessage())
|
.append(new LiteralText(finalThrowable.getCause().getMessage())
|
||||||
.formatted(Formatting.GRAY)));
|
.formatted(Formatting.GRAY)));
|
||||||
}
|
}
|
||||||
|
|
@ -143,21 +153,29 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
||||||
CommandExecutionException.class,
|
CommandExecutionException.class,
|
||||||
(CommandExecutionException) throwable,
|
(CommandExecutionException) throwable,
|
||||||
(c, e) -> {
|
(c, e) -> {
|
||||||
source.sendError(this.decorateHoverStacktrace(
|
this.sendError.accept(source, this.decorateHoverStacktrace(
|
||||||
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
||||||
finalThrowable.getCause(),
|
finalThrowable.getCause(),
|
||||||
sender
|
sender
|
||||||
));
|
));
|
||||||
LOGGER.warn("Error occurred while executing command for user {}:", source.getName(), finalThrowable);
|
LOGGER.warn(
|
||||||
|
"Error occurred while executing command for user {}:",
|
||||||
|
this.getName.apply(source),
|
||||||
|
finalThrowable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
source.sendError(this.decorateHoverStacktrace(
|
this.sendError.accept(source, this.decorateHoverStacktrace(
|
||||||
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
||||||
throwable,
|
throwable,
|
||||||
sender
|
sender
|
||||||
));
|
));
|
||||||
LOGGER.warn("Error occurred while executing command for user {}:", source.getName(), throwable);
|
LOGGER.warn(
|
||||||
|
"Error occurred while executing command for user {}:",
|
||||||
|
this.getName.apply(source),
|
||||||
|
throwable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg & 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 cloud.commandframework.fabric;
|
||||||
|
|
||||||
|
import cloud.commandframework.CommandTree;
|
||||||
|
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator;
|
||||||
|
import cloud.commandframework.execution.CommandExecutionCoordinator;
|
||||||
|
import cloud.commandframework.meta.CommandMeta;
|
||||||
|
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
|
||||||
|
import net.minecraft.server.command.CommandManager;
|
||||||
|
import net.minecraft.server.command.CommandOutput;
|
||||||
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import net.minecraft.text.LiteralText;
|
||||||
|
import net.minecraft.util.math.Vec2f;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A command manager for registering server-side commands.
|
||||||
|
*
|
||||||
|
* <p>All commands should be registered within mod initializers. Any registrations occurring after the first call to
|
||||||
|
* {@link CommandRegistrationCallback} will be considered <em>unsafe</em>, and will only be permitted when the unsafe
|
||||||
|
* registration manager option is enabled.</p>
|
||||||
|
*
|
||||||
|
* @param <C> the command sender type
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
public final class FabricServerCommandManager<C> extends FabricCommandManager<C, ServerCommandSource> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A meta attribute specifying which environments a command should be registered in.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link CommandManager.RegistrationEnvironment#ALL}.</p>
|
||||||
|
*/
|
||||||
|
public static final CommandMeta.Key<CommandManager.RegistrationEnvironment> META_REGISTRATION_ENVIRONMENT = CommandMeta.Key.of(
|
||||||
|
CommandManager.RegistrationEnvironment.class,
|
||||||
|
"cloud:registration-environment"
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a command manager using native source types.
|
||||||
|
*
|
||||||
|
* @param execCoordinator Execution coordinator instance.
|
||||||
|
* @return a new command manager
|
||||||
|
* @see #FabricServerCommandManager(Function, Function, Function) for a more thorough explanation
|
||||||
|
*/
|
||||||
|
public static FabricServerCommandManager<ServerCommandSource> createNative(
|
||||||
|
final Function<CommandTree<ServerCommandSource>, CommandExecutionCoordinator<ServerCommandSource>> execCoordinator
|
||||||
|
) {
|
||||||
|
return new FabricServerCommandManager<>(execCoordinator, Function.identity(), Function.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command manager instance.
|
||||||
|
*
|
||||||
|
* @param commandExecutionCoordinator Execution coordinator instance. The coordinator is in charge of executing incoming
|
||||||
|
* commands. Some considerations must be made when picking a suitable execution coordinator
|
||||||
|
* for your platform. For example, an entirely asynchronous coordinator is not suitable
|
||||||
|
* when the parsers used in that particular platform are not thread safe. If you have
|
||||||
|
* commands that perform blocking operations, however, it might not be a good idea to
|
||||||
|
* use a synchronous execution coordinator. In most cases you will want to pick between
|
||||||
|
* {@link CommandExecutionCoordinator#simpleCoordinator()} and
|
||||||
|
* {@link AsynchronousCommandExecutionCoordinator}
|
||||||
|
* @param commandSourceMapper Function that maps {@link ServerCommandSource} to the command sender type
|
||||||
|
* @param backwardsCommandSourceMapper Function that maps the command sender type to {@link ServerCommandSource}
|
||||||
|
*/
|
||||||
|
public FabricServerCommandManager(
|
||||||
|
final @NonNull Function<@NonNull CommandTree<C>, @NonNull CommandExecutionCoordinator<C>> commandExecutionCoordinator,
|
||||||
|
final Function<ServerCommandSource, C> commandSourceMapper,
|
||||||
|
final Function<C, ServerCommandSource> backwardsCommandSourceMapper
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
commandExecutionCoordinator,
|
||||||
|
commandSourceMapper,
|
||||||
|
backwardsCommandSourceMapper,
|
||||||
|
new FabricCommandRegistrationHandler.Server<>(),
|
||||||
|
() -> new ServerCommandSource(
|
||||||
|
CommandOutput.DUMMY,
|
||||||
|
Vec3d.ZERO,
|
||||||
|
Vec2f.ZERO,
|
||||||
|
null,
|
||||||
|
4,
|
||||||
|
"",
|
||||||
|
LiteralText.EMPTY,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a sender has a certain permission.
|
||||||
|
*
|
||||||
|
* <p>The current implementation checks op level, pending a full Fabric permissions api.</p>
|
||||||
|
*
|
||||||
|
* @param sender Command sender
|
||||||
|
* @param permission Permission node
|
||||||
|
* @return whether the sender has the specified permission
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(@NonNull final C sender, @NonNull final String permission) {
|
||||||
|
final ServerCommandSource source = this.getBackwardsCommandSourceMapper().apply(sender);
|
||||||
|
return source.hasPermissionLevel(source.getMinecraftServer().getOpPermissionLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -29,7 +29,7 @@ import cloud.commandframework.arguments.CommandArgument;
|
||||||
import cloud.commandframework.arguments.standard.IntegerArgument;
|
import cloud.commandframework.arguments.standard.IntegerArgument;
|
||||||
import cloud.commandframework.arguments.standard.StringArgument;
|
import cloud.commandframework.arguments.standard.StringArgument;
|
||||||
import cloud.commandframework.execution.CommandExecutionCoordinator;
|
import cloud.commandframework.execution.CommandExecutionCoordinator;
|
||||||
import cloud.commandframework.fabric.FabricCommandManager;
|
import cloud.commandframework.fabric.FabricServerCommandManager;
|
||||||
import cloud.commandframework.meta.CommandMeta;
|
import cloud.commandframework.meta.CommandMeta;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.internal.Streams;
|
import com.google.gson.internal.Streams;
|
||||||
|
|
@ -62,8 +62,8 @@ public final class FabricExample implements ModInitializer {
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
// Create a commands manager. We'll use native command source types for this.
|
// Create a commands manager. We'll use native command source types for this.
|
||||||
|
|
||||||
final FabricCommandManager<ServerCommandSource> manager =
|
final FabricServerCommandManager<ServerCommandSource> manager =
|
||||||
FabricCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator());
|
FabricServerCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator());
|
||||||
|
|
||||||
final Command.Builder<ServerCommandSource> base = manager.commandBuilder("cloudtest");
|
final Command.Builder<ServerCommandSource> base = manager.commandBuilder("cloudtest");
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ public final class FabricExample implements ModInitializer {
|
||||||
|
|
||||||
manager.command(base.literal("dump")
|
manager.command(base.literal("dump")
|
||||||
.meta(CommandMeta.DESCRIPTION, "Dump the client's Brigadier command tree (integrated server only)")
|
.meta(CommandMeta.DESCRIPTION, "Dump the client's Brigadier command tree (integrated server only)")
|
||||||
.meta(FabricCommandManager.META_REGISTRATION_ENVIRONMENT, CommandManager.RegistrationEnvironment.INTEGRATED)
|
.meta(FabricServerCommandManager.META_REGISTRATION_ENVIRONMENT, CommandManager.RegistrationEnvironment.INTEGRATED)
|
||||||
.handler(ctx -> {
|
.handler(ctx -> {
|
||||||
final Path target =
|
final Path target =
|
||||||
FabricLoader.getInstance().getGameDir().resolve(
|
FabricLoader.getInstance().getGameDir().resolve(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue