From 48181164b0bf581bb90859e0d6961555466fed41 Mon Sep 17 00:00:00 2001 From: Zach Levis Date: Sun, 3 Jan 2021 19:02:48 -0800 Subject: [PATCH] fabric: Split out server-specific command manager to allow for client commands --- .../fabric/FabricCommandManager.java | 118 +++++++--------- .../FabricCommandRegistrationHandler.java | 93 ++++++++----- .../fabric/FabricExecutor.java | 52 ++++--- .../fabric/FabricServerCommandManager.java | 130 ++++++++++++++++++ .../fabric/testmod/FabricExample.java | 8 +- 5 files changed, 273 insertions(+), 128 deletions(-) create mode 100644 cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandManager.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandManager.java index cf6e7c35..2518d6a5 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandManager.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandManager.java @@ -37,6 +37,7 @@ import cloud.commandframework.meta.CommandMeta; import cloud.commandframework.meta.SimpleCommandMeta; import com.mojang.brigadier.arguments.ArgumentType; import io.leangen.geantyref.TypeToken; +import net.minecraft.command.CommandSource; import net.minecraft.command.argument.AngleArgumentType; import net.minecraft.command.argument.BlockPredicateArgumentType; import net.minecraft.command.argument.ColorArgumentType; @@ -61,48 +62,34 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.particle.ParticleEffect; import net.minecraft.predicate.NumberRange; 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.text.LiteralText; import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; 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 java.util.EnumSet; import java.util.function.Function; +import java.util.function.Supplier; -public class FabricCommandManager extends CommandManager implements BrigadierManagerHolder { +/** + * A command manager for either the server or client on Fabric. + * + *

Commands registered with managers of this type will be registered into a Brigadier command tree.

+ * + *

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}.

+ * + * @param the manager's sender type + * @param the platform sender type + * @see FabricServerCommandManager for server commands + */ +public abstract class FabricCommandManager extends CommandManager implements BrigadierManagerHolder { - /** - * A meta attribute specifying which environments a command should be registered in. - * - *

The default value is {@link RegistrationEnvironment#ALL}.

- */ - public static final CommandMeta.Key META_REGISTRATION_ENVIRONMENT = CommandMeta.Key.of( - RegistrationEnvironment.class, - "cloud:registration-environment" - ); - - private final Function commandSourceMapper; - private final Function backwardsCommandSourceMapper; - private final CloudBrigadierManager 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 createNative( - final Function, CommandExecutionCoordinator> execCoordinator - ) { - return new FabricCommandManager<>(execCoordinator, Function.identity(), Function.identity()); - } + private final Function commandSourceMapper; + private final Function backwardsCommandSourceMapper; + private final CloudBrigadierManager brigadierManager; /** @@ -118,14 +105,18 @@ public class FabricCommandManager extends CommandManager implements Brigad * {@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} + * @param registrationHandler the handler accepting command registrations + * @param dummyCommandSourceProvider a provider of a dummy command source, for use with brigadier registration */ @SuppressWarnings("unchecked") - protected FabricCommandManager( + FabricCommandManager( final @NonNull Function<@NonNull CommandTree, @NonNull CommandExecutionCoordinator> commandExecutionCoordinator, - final Function commandSourceMapper, - final Function backwardsCommandSourceMapper - ) { - super(commandExecutionCoordinator, new FabricCommandRegistrationHandler<>()); + final Function commandSourceMapper, + final Function backwardsCommandSourceMapper, + final FabricCommandRegistrationHandler registrationHandler, + final Supplier dummyCommandSourceProvider + ) { + super(commandExecutionCoordinator, registrationHandler); this.commandSourceMapper = commandSourceMapper; this.backwardsCommandSourceMapper = backwardsCommandSourceMapper; @@ -133,26 +124,16 @@ public class FabricCommandManager extends CommandManager implements Brigad this.brigadierManager = new CloudBrigadierManager<>(this, () -> new CommandContext<>( // 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 - this.commandSourceMapper.apply(new ServerCommandSource( - CommandOutput.DUMMY, - Vec3d.ZERO, - Vec2f.ZERO, - null, - 4, - "", - LiteralText.EMPTY, - null, - null - )), - this + this.commandSourceMapper.apply(dummyCommandSourceProvider.get()), + this )); this.brigadierManager.backwardsBrigadierSenderMapper(this.backwardsCommandSourceMapper); this.registerNativeBrigadierMappings(this.brigadierManager); - ((FabricCommandRegistrationHandler) this.getCommandRegistrationHandler()).initialize(this); + ((FabricCommandRegistrationHandler) this.getCommandRegistrationHandler()).initialize(this); } - private void registerNativeBrigadierMappings(final CloudBrigadierManager brigadier) { + private void registerNativeBrigadierMappings(final CloudBrigadierManager brigadier) { /* Cloud-native argument types */ brigadier.registerMapping(new TypeToken>() {}, false, cloud -> UuidArgumentType.uuid()); @@ -200,27 +181,26 @@ public class FabricCommandManager extends CommandManager implements Brigad // entity argument type: single or multiple, players or any entity -- returns EntitySelector, but do we want that? } - private void registerConstantNativeParserSupplier(final Class type, final ArgumentType 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 value type + */ + final void registerConstantNativeParserSupplier(final Class type, final ArgumentType argument) { this.registerConstantNativeParserSupplier(TypeToken.get(type), argument); } - private void registerConstantNativeParserSupplier(final TypeToken type, final ArgumentType 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. * - *

The current implementation checks op level, pending a full Fabric permissions api.

- * - * @param sender Command sender - * @param permission Permission node - * @return whether the sender has the specified permission + * @param type the Java type to map + * @param argument the Brigadier parser + * @param value type */ - @Override - public boolean hasPermission(@NonNull final C sender, @NonNull final String permission) { - final ServerCommandSource source = this.backwardsCommandSourceMapper.apply(sender); - return source.hasPermissionLevel(source.getMinecraftServer().getOpPermissionLevel()); + final void registerConstantNativeParserSupplier(final TypeToken type, final ArgumentType argument) { + this.getParserRegistry().registerParserSupplier(type, params -> new WrappedBrigadierParser<>(argument)); } @Override @@ -233,7 +213,7 @@ public class FabricCommandManager extends CommandManager implements Brigad * * @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; } @@ -242,12 +222,12 @@ public class FabricCommandManager extends CommandManager implements Brigad * * @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; } @Override - public final @NonNull CloudBrigadierManager brigadierManager() { + public final @NonNull CloudBrigadierManager brigadierManager() { return this.brigadierManager; } diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandRegistrationHandler.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandRegistrationHandler.java index c48c59b6..00987e8b 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandRegistrationHandler.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandRegistrationHandler.java @@ -31,6 +31,7 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.RootCommandNode; 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.RegistrationEnvironment; import net.minecraft.server.command.ServerCommandSource; @@ -43,57 +44,73 @@ import java.util.concurrent.ConcurrentHashMap; /** * A registration handler for Fabric API. * - *

If the command registration callback has already been called, this will attempt - * to register with the active server's command dispatcher.

+ *

Subtypes exist for client and server commands.

* * @param command sender type + * @param native sender type */ -public final class FabricCommandRegistrationHandler implements CommandRegistrationHandler { - private @MonotonicNonNull FabricCommandManager commandManager; - private final Set> registeredCommands = ConcurrentHashMap.newKeySet(); +abstract class FabricCommandRegistrationHandler implements CommandRegistrationHandler { + private @MonotonicNonNull FabricCommandManager commandManager; - void initialize(final FabricCommandManager manager) { + void initialize(final FabricCommandManager manager) { this.commandManager = manager; - CommandRegistrationCallback.EVENT.register(this::registerAllCommands); } - @Override - @SuppressWarnings("unchecked") - public boolean registerCommand(@NonNull final Command command) { - return this.registeredCommands.add((Command) command); + FabricCommandManager getCommandManager() { + return this.commandManager; } - private void registerAllCommands(final CommandDispatcher dispatcher, final boolean isDedicated) { - this.commandManager.registrationCalled(); - for (final Command command : this.registeredCommands) { - /* Only register commands in the declared environment */ - final RegistrationEnvironment env = command.getCommandMeta().getOrDefault( - FabricCommandManager.META_REGISTRATION_ENVIRONMENT, - RegistrationEnvironment.ALL - ); + static class Server extends FabricCommandRegistrationHandler { + private final Set> registeredCommands = ConcurrentHashMap.newKeySet(); - if ((env == RegistrationEnvironment.INTEGRATED && isDedicated) - || (env == RegistrationEnvironment.DEDICATED && !isDedicated)) { - continue; - } - this.registerCommand(dispatcher.getRoot(), command); + @Override + void initialize(final FabricCommandManager manager) { + super.initialize(manager); + CommandRegistrationCallback.EVENT.register(this::registerAllCommands); } - } - private void registerCommand(final RootCommandNode dispatcher, final Command command) { + @Override @SuppressWarnings("unchecked") - final StaticArgument first = ((StaticArgument) command.getArguments().get(0)); - final CommandNode baseNode = this.commandManager.brigadierManager().createLiteralCommandNode( - 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()); + public boolean registerCommand(@NonNull final Command command) { + return this.registeredCommands.add((Command) command); } + + private void registerAllCommands(final CommandDispatcher dispatcher, final boolean isDedicated) { + this.getCommandManager().registrationCalled(); + for (final Command 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 dispatcher, final Command command) { + @SuppressWarnings("unchecked") + final StaticArgument first = ((StaticArgument) command.getArguments().get(0)); + final CommandNode 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()); + } + } + } } diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricExecutor.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricExecutor.java index ea529b24..15ebefd6 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricExecutor.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricExecutor.java @@ -34,7 +34,7 @@ import cloud.commandframework.execution.CommandResult; import com.mojang.brigadier.Command; import com.mojang.brigadier.context.CommandContext; 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.HoverEvent; import net.minecraft.text.LiteralText; @@ -50,8 +50,9 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.concurrent.CompletionException; import java.util.function.BiConsumer; +import java.util.function.Function; -final class FabricExecutor implements Command { +final class FabricExecutor implements Command { private static final Logger LOGGER = LogManager.getLogger(); private static final Text NEWLINE = new LiteralText("\n"); @@ -61,15 +62,23 @@ final class FabricExecutor implements Command { + "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 final FabricCommandManager manager; + private final FabricCommandManager manager; + private final Function getName; + private final BiConsumer sendError; - FabricExecutor(final @NonNull FabricCommandManager manager) { + FabricExecutor( + final @NonNull FabricCommandManager manager, + final @NonNull Function getName, + final @NonNull BiConsumer sendError + ) { this.manager = manager; + this.getName = getName; + this.sendError = sendError; } @Override - public int run(final @NonNull CommandContext ctx) { - final ServerCommandSource source = ctx.getSource(); + public int run(final @NonNull CommandContext ctx) { + final S source = ctx.getSource(); final String input = ctx.getInput().substring(1); final C sender = this.manager.getCommandSourceMapper().apply(source); this.manager.executeCommand(sender, input).whenComplete(this.getResultConsumer(source, sender)); @@ -77,7 +86,7 @@ final class FabricExecutor implements Command { } private @NonNull BiConsumer<@NonNull CommandResult, ? super Throwable> getResultConsumer( - final @NonNull ServerCommandSource source, + final @NonNull S source, final @NonNull C sender ) { return (result, throwable) -> { @@ -92,7 +101,8 @@ final class FabricExecutor implements Command { InvalidSyntaxException.class, (InvalidSyntaxException) throwable, (c, e) -> - source.sendError( + this.sendError.accept( + source, new LiteralText("Invalid Command Syntax. Correct command syntax is: ") .append(new LiteralText(e.getCorrectSyntax()) .styled(style -> style.withColor(Formatting.GRAY)))) @@ -103,21 +113,21 @@ final class FabricExecutor implements Command { InvalidCommandSenderException.class, (InvalidCommandSenderException) throwable, (c, e) -> - source.sendError(new LiteralText(finalThrowable.getMessage())) + this.sendError.accept(source, new LiteralText(finalThrowable.getMessage())) ); } else if (throwable instanceof NoPermissionException) { this.manager.handleException( sender, NoPermissionException.class, (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) { this.manager.handleException( sender, NoSuchCommandException.class, (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) { this.manager.handleException( @@ -126,12 +136,12 @@ final class FabricExecutor implements Command { (ArgumentParseException) throwable, (c, e) -> { 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(Texts.toText(((CommandSyntaxException) finalThrowable.getCause()).getRawMessage())) .formatted(Formatting.GRAY))); } else { - source.sendError(new LiteralText("Invalid Command Argument: ") + this.sendError.accept(source, new LiteralText("Invalid Command Argument: ") .append(new LiteralText(finalThrowable.getCause().getMessage()) .formatted(Formatting.GRAY))); } @@ -143,21 +153,29 @@ final class FabricExecutor implements Command { CommandExecutionException.class, (CommandExecutionException) throwable, (c, e) -> { - source.sendError(this.decorateHoverStacktrace( + this.sendError.accept(source, this.decorateHoverStacktrace( new LiteralText(MESSAGE_INTERNAL_ERROR), finalThrowable.getCause(), 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 { - source.sendError(this.decorateHoverStacktrace( + this.sendError.accept(source, this.decorateHoverStacktrace( new LiteralText(MESSAGE_INTERNAL_ERROR), throwable, 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 + ); } } }; diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java new file mode 100644 index 00000000..b9156539 --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java @@ -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. + * + *

All commands should be registered within mod initializers. Any registrations occurring after the first call to + * {@link CommandRegistrationCallback} will be considered unsafe, and will only be permitted when the unsafe + * registration manager option is enabled.

+ * + * @param the command sender type + * @since 1.4.0 + */ +public final class FabricServerCommandManager extends FabricCommandManager { + + /** + * A meta attribute specifying which environments a command should be registered in. + * + *

The default value is {@link CommandManager.RegistrationEnvironment#ALL}.

+ */ + public static final CommandMeta.Key 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 createNative( + final Function, CommandExecutionCoordinator> 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, @NonNull CommandExecutionCoordinator> commandExecutionCoordinator, + final Function commandSourceMapper, + final Function 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. + * + *

The current implementation checks op level, pending a full Fabric permissions api.

+ * + * @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()); + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java index a4403bc8..dcc1cbd8 100644 --- a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java +++ b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java @@ -29,7 +29,7 @@ import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.standard.IntegerArgument; import cloud.commandframework.arguments.standard.StringArgument; import cloud.commandframework.execution.CommandExecutionCoordinator; -import cloud.commandframework.fabric.FabricCommandManager; +import cloud.commandframework.fabric.FabricServerCommandManager; import cloud.commandframework.meta.CommandMeta; import com.google.gson.JsonObject; import com.google.gson.internal.Streams; @@ -62,8 +62,8 @@ public final class FabricExample implements ModInitializer { public void onInitialize() { // Create a commands manager. We'll use native command source types for this. - final FabricCommandManager manager = - FabricCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator()); + final FabricServerCommandManager manager = + FabricServerCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator()); final Command.Builder base = manager.commandBuilder("cloudtest"); @@ -85,7 +85,7 @@ public final class FabricExample implements ModInitializer { manager.command(base.literal("dump") .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 -> { final Path target = FabricLoader.getInstance().getGameDir().resolve(