fabric: Begin work on commands manager for platform

This commit is contained in:
Zach Levis 2020-11-24 21:39:29 -08:00 committed by Jason
parent 0722bf6ead
commit eef98da9c9
16 changed files with 737 additions and 8 deletions

View file

@ -9,12 +9,14 @@ This directory hosts Minecraft specific implementations of cloud. Their features
| `cloud-bungee` | BungeeCord | 1.8+ | No |
| `cloud-velocity` | Velocity 1.1.0 | 1.7+ | Yes |
| `cloud-cloudburst` | CloudBurst 1.0.0 | Bedrock 1.16.20+ | No |
| `cloud-fabric` | Minecraft, via Fabric | 1.16+ | Yes |
There is also a `cloud-minecraft-extras` module that contains a few extra minecraft related features
## cloud-bukkit
Bukkit mappings for cloud. If `commodore` is present on the classpath and the server is running at least version 1.13+, Brigadier mappings will be available.
Bukkit mappings for cloud. If `commodore` is present on the classpath and the server is running at least version 1.13+, Brigadier
mappings will be available.
### dependency
**maven**:
@ -39,7 +41,10 @@ Simply do:
final BukkitCommandManager<YourSender> bukkitCommandManager = new BukkitCommandManager<>(
yourPlugin, yourExecutionCoordinator, forwardMapper, backwardsMapper);
```
The `forwardMapper` is a function that maps your chosen sender type to Bukkit's [CommandSender](https://jd.bukkit.org/org/bukkit/command/CommandSender.html), and the `backwardsMapper`does the opposite. In the case that you don't need a custom sender type, you can simply use `CommandSender`as the generic type and pass `Function.identity()` as the forward and backward mappers.
The `forwardMapper` is a function that maps your chosen sender type to Bukkit's
[CommandSender](https://jd.bukkit.org/org/bukkit/command/CommandSender.html), and the `backwardsMapper` does the opposite. In
the case that you don't need a custom sender type, you can simply use `CommandSender`as the generic type
and pass `Function.identity()` as the forward and backward mappers.
### commodore
To use commodore, include it as a dependency:
@ -78,7 +83,9 @@ You can check whether or not the running server supports Brigadier, by using `bu
An example plugin using the `cloud-paper` API can be found [here](https://github.com/Sauilitired/cloud/tree/master/examples/example-bukkit).
`cloud-paper`works on all Bukkit derivatives and has graceful fallbacks for cases where Paper specific features are missing. It is initialized the same way as the Bukkit manager, except `PaperCommandManager`is used instead. When using Paper 1.15+ Brigadier mappings are available even without commodore present.
`cloud-paper`works on all Bukkit derivatives and has graceful fallbacks for cases where Paper specific features are missing. It
is initialized the same way as the Bukkit manager, except `PaperCommandManager`is used instead. When using Paper 1.15+ Brigadier
mappings are available even without commodore present.
### dependency
**maven**:
@ -98,7 +105,9 @@ dependencies {
```
### asynchronous completions
`cloud-paper`supports asynchronous completions when running on Paper. First check if the capability is present, by using `paperCommandManager.queryCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)` and then initialize the asynchronous completion listener by using `paperCommandManager.registerAsynchronousCompletions()`.
`cloud-paper`supports asynchronous completions when running on Paper. First check if the capability is present, by using
`paperCommandManager.queryCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)` and then initialize the asynchronous
completion listener by using `paperCommandManager.registerAsynchronousCompletions()`.
## cloud-bungee
BungeeCord mappings for cloud.
@ -126,7 +135,10 @@ Simply do:
final BungeeCommandManager<YourSender> bungeeCommandManager = new BungeeCommandManager<>(
yourPlugin, yourExecutionCoordinator, forwardMapper, backwardsMapper);
```
The `forwardMapper` is a function that maps your chosen sender type to Bungee's [CommandSender](https://ci.md-5.net/job/BungeeCord/ws/api/target/apidocs/net/md_5/bungee/api/CommandSender.html), and the `backwardsMapper`does the opposite. In the case that you don't need a custom sender type, you can simply use `CommandSender`as the generic type and pass `Function.identity()` as the forward and backward mappers.
The `forwardMapper` is a function that maps your chosen sender type to Bungee's
[CommandSender](https://ci.md-5.net/job/BungeeCord/ws/api/target/apidocs/net/md_5/bungee/api/CommandSender.html), and
the `backwardsMapper`does the opposite. In the case that you don't need a custom sender type, you can simply use `CommandSender`as
the generic type and pass `Function.identity()` as the forward and backward mappers.
## cloud-velocity
@ -154,7 +166,10 @@ Simply do:
final VelocityCommandManager<YourSender> velocityCommandManager = new VelocityCommandManager<>(
proxyServer, yourExecutionCoordinator, forwardMapper, backwardsMapper);
```
The `forwardMapper` is a function that maps your chosen sender type to Velocity's [CommandSource](https://jd.velocitypowered.com/1.1.0/com/velocitypowered/api/command/CommandSource.html), and the `backwardsMapper`does the opposite. In the case that you don't need a custom sender type, you can simply use `CommandSource`as the generic type and pass `Function.identity()` as the forward and backward mappers.
The `forwardMapper` is a function that maps your chosen sender type to Velocity's
[CommandSource](https://jd.velocitypowered.com/1.1.0/com/velocitypowered/api/command/CommandSource.html), and the
`backwardsMapper` does the opposite. In the case that you don't need a custom sender type, you can simply use `CommandSource`as
the generic type and pass `Function.identity()` as the forward and backward mappers.
## cloud-cloudburst
@ -182,4 +197,35 @@ Simply do:
final CloudburstCommandManager<YourSender> cloudburstCommandManager = new CloudburstCommandManager<>(
yourPlugin, yourExecutionCoordinator, forwardMapper, backwardsMapper);
```
The `forwardMapper` is a function that maps your chosen sender type to Cloudbursts's [CommandSender](https://ci.nukkitx.com/job/NukkitX/job/Nukkit/job/master/javadoc/cn/nukkit/command/CommandSender.html), and the `backwardsMapper`does the opposite. In the case that you don't need a custom sender type, you can simply use `CommandSource`as the generic type and pass `Function.identity()` as the forward and backward mappers.
The `forwardMapper` is a function that maps your chosen sender type to Cloudbursts's
[CommandSender](https://ci.nukkitx.com/job/NukkitX/job/Nukkit/job/master/javadoc/cn/nukkit/command/CommandSender.html), and the
`backwardsMapper` does the opposite. In the case that you don't need a custom sender type, you can simply use `CommandSource` as
the generic type and pass `Function.identity()` as the forward and backward mappers.
## cloud-fabric
cloud mappings for the Fabric mod loader for Minecraft 1.16+
### dependency
**gradle**:
```groovy
dependencies {
modImplementation 'cloud.commandframework:cloud-fabric:1.3.0-SNAPSHOT'
}
```
Simply do:
```java
final FabricCommandManager<YourSender> fabricCommandManager = new FabricCommandManager<>(
yourExecutionCoordinator, forwarMapper, backwardsMapper);
```
The `forwardMapper` is a function that maps your chosen sender type to Minecraft's
[ServerCommandSource](https://maven.fabricmc.net/docs/yarn-1.16.4+build.7/net/minecraft/server/command/ServerCommandSource.html),
and the `backwardsMapper` does the opposite.
In the case that you don't need a custom sender type, you can use the helper method
`FabricCommandManager.createNative(yourExecutionCoordinator)` instead, which will create a command manager that works directly
with `ServerCommandSource`s.

View file

@ -0,0 +1,73 @@
plugins {
id("fabric-loom") version "0.5-SNAPSHOT"
}
// Set up a testmod source set
val testmod by sourceSets.creating {
val main = sourceSets.main.get()
compileClasspath += main.compileClasspath
runtimeClasspath += main.runtimeClasspath
dependencies.add(implementationConfigurationName, main.output)
}
val testmodJar by tasks.creating(Jar::class) {
archiveClassifier.set("testmod-dev")
group = LifecycleBasePlugin.BUILD_GROUP
from(testmod.output)
}
loom.unmappedModCollection.from(testmodJar)
tasks.withType(ProcessResources::class).configureEach {
inputs.property("version", project.version)
filesMatching("fabric.mod.json") {
expand("version" to project.version)
}
}
tasks.withType(Javadoc::class).configureEach {
(options as? StandardJavadocDocletOptions)?.apply {
links("https://maven.fabricmc.net/docs/yarn-${Versions.fabricMc}+build.${Versions.fabricYarn}/")
}
}
dependencies {
minecraft("com.mojang:minecraft:${Versions.fabricMc}")
mappings("net.fabricmc:yarn:${Versions.fabricMc}+build.${Versions.fabricYarn}:v2")
modImplementation("net.fabricmc:fabric-loader:${Versions.fabricLoader}")
modImplementation(fabricApi.module("fabric-command-api-v1", Versions.fabricApi))
api(include(project(":cloud-core"))!!)
implementation(include(project(":cloud-brigadier"))!!)
include(project(":cloud-services"))
include("io.leangen.geantyref:geantyref:${Versions.geantyref}")
}
indra {
includeJavaSoftwareComponentInPublications.set(false)
configurePublications {
// add all the jars that should be included when publishing to maven
artifact(tasks.remapJar) {
builtBy(tasks.remapJar)
}
artifact(tasks.sourcesJar) {
builtBy(tasks.remapSourcesJar)
}
// Loom is broken with project dependencies in the same build (because it resolves dependencies during configuration)
// Please look away
pom {
withXml {
val dependencies = asNode().appendNode("dependencies")
sequenceOf("brigadier", "core").forEach {
val depNode = dependencies.appendNode("dependency")
depNode.appendNode("groupId", project.group)
depNode.appendNode("artifactId", "cloud-$it")
depNode.appendNode("version", project.version)
depNode.appendNode("scope", "compile")
}
}
}
}
}

View file

@ -0,0 +1,152 @@
//
// 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.CommandManager;
import cloud.commandframework.CommandTree;
import cloud.commandframework.brigadier.BrigadierManagerHolder;
import cloud.commandframework.brigadier.CloudBrigadierManager;
import cloud.commandframework.context.CommandContext;
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta;
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;
public class FabricCommandManager<C> extends CommandManager<C> implements BrigadierManagerHolder<C> {
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());
}
/**
* 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}
*/
@SuppressWarnings("unchecked")
protected FabricCommandManager(
final @NonNull Function<@NonNull CommandTree<C>, @NonNull CommandExecutionCoordinator<C>> commandExecutionCoordinator,
final Function<ServerCommandSource, C> commandSourceMapper,
final Function<C, ServerCommandSource> backwardsCommandSourceMapper
) {
super(commandExecutionCoordinator, new FabricCommandRegistrationHandler<>());
this.commandSourceMapper = commandSourceMapper;
this.backwardsCommandSourceMapper = backwardsCommandSourceMapper;
// We're always brigadier
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.getCaptionRegistry()
));
((FabricCommandRegistrationHandler<C>) this.getCommandRegistrationHandler()).initialize(this);
}
/**
* 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.backwardsCommandSourceMapper.apply(sender);
return source.hasPermissionLevel(source.getMinecraftServer().getOpPermissionLevel());
}
@Override
public final @NonNull CommandMeta createDefaultCommandMeta() {
return SimpleCommandMeta.empty();
}
/**
* Gets the mapper from a game {@link ServerCommandSource} to the manager's {@code C} type.
*
* @return Command source mapper
*/
public final @NonNull Function<@NonNull ServerCommandSource, @NonNull C> getCommandSourceMapper() {
return this.commandSourceMapper;
}
/**
* Gets the mapper from the manager's {@code C} type to a game {@link ServerCommandSource}.
*
* @return Command source mapper
*/
public final @NonNull Function<@NonNull C, @NonNull ServerCommandSource> getBackwardsCommandSourceMapper() {
return this.backwardsCommandSourceMapper;
}
@Override
public final @NonNull CloudBrigadierManager<C, ServerCommandSource> brigadierManager() {
return this.brigadierManager;
}
}

View file

@ -0,0 +1,92 @@
//
// 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.Command;
import cloud.commandframework.arguments.StaticArgument;
import cloud.commandframework.internal.CommandRegistrationHandler;
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.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* A registration handler for Fabric API.
*
* <p>If the command registration callback has already been called, this will attempt
* to register with the active server's command dispatcher.</p>
*
* @param <C> command sender type
*/
public final class FabricCommandRegistrationHandler<C> implements CommandRegistrationHandler {
private @MonotonicNonNull FabricCommandManager<C> commandManager;
private final Set<Command<C>> registeredCommands = ConcurrentHashMap.newKeySet();
private boolean commandRegistrationCalled;
void initialize(final FabricCommandManager<C> manager) {
this.commandManager = manager;
CommandRegistrationCallback.EVENT.register(this::registerAllCommands);
}
@Override
@SuppressWarnings("unchecked")
public boolean registerCommand(@NonNull final Command<?> command) {
if (this.commandRegistrationCalled) {
throw new IllegalStateException("too late!");
}
return this.registeredCommands.add((Command<C>) command);
}
private void registerAllCommands(final CommandDispatcher<ServerCommandSource> dispatcher, final boolean isDedicated) {
this.commandRegistrationCalled = true;
for (final Command<C> command : this.registeredCommands) {
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.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());
}
}
}

View file

@ -0,0 +1,174 @@
//
// 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.exceptions.ArgumentParseException;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException;
import cloud.commandframework.exceptions.NoSuchCommandException;
import cloud.commandframework.execution.CommandResult;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.LiteralText;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.CompletionException;
import java.util.function.BiConsumer;
final class FabricExecutor<C> implements Command<ServerCommandSource> {
private static final Text NEWLINE = new LiteralText("\n");
private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_NO_PERMS =
"I'm sorry, but you do not have permission to perform this 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<C> manager;
FabricExecutor(final @NonNull FabricCommandManager<C> manager) {
this.manager = manager;
}
@Override
public int run(final @NonNull CommandContext<ServerCommandSource> ctx) {
final ServerCommandSource 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));
return com.mojang.brigadier.Command.SINGLE_SUCCESS;
}
private @NonNull BiConsumer<@NonNull CommandResult<C>, ? super Throwable> getResultConsumer(
final @NonNull ServerCommandSource source,
final @NonNull C sender
) {
return (result, throwable) -> {
if (throwable != null) {
if (throwable instanceof CompletionException) {
throwable = throwable.getCause();
}
final Throwable finalThrowable = throwable;
if (throwable instanceof InvalidSyntaxException) {
this.manager.handleException(
sender,
InvalidSyntaxException.class,
(InvalidSyntaxException) throwable,
(c, e) ->
source.sendError(
new LiteralText("Invalid Command Syntax. Correct command syntax is: ")
.append(new LiteralText(e.getCorrectSyntax())
.styled(style -> style.withColor(Formatting.GRAY))))
);
} else if (throwable instanceof InvalidCommandSenderException) {
this.manager.handleException(
sender,
InvalidCommandSenderException.class,
(InvalidCommandSenderException) throwable,
(c, e) ->
source.sendError(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))
);
} else if (throwable instanceof NoSuchCommandException) {
this.manager.handleException(
sender,
NoSuchCommandException.class,
(NoSuchCommandException) throwable,
(c, e) -> source.sendError(new LiteralText(MESSAGE_UNKNOWN_COMMAND))
);
} else if (throwable instanceof ArgumentParseException) {
this.manager.handleException(
sender,
ArgumentParseException.class,
(ArgumentParseException) throwable,
(c, e) -> source.sendError(new LiteralText("Invalid Command Argument: ")
.append(new LiteralText(finalThrowable.getCause().getMessage())
.styled(style -> style.withColor(Formatting.GRAY))))
);
} else if (throwable instanceof CommandExecutionException) {
this.manager.handleException(
sender,
CommandExecutionException.class,
(CommandExecutionException) throwable,
(c, e) -> {
source.sendError(decorateHoverStacktrace(
new LiteralText(MESSAGE_INTERNAL_ERROR),
finalThrowable.getCause(),
sender
));
finalThrowable.getCause().printStackTrace();
}
);
} else {
source.sendError(decorateHoverStacktrace(
new LiteralText(MESSAGE_INTERNAL_ERROR),
throwable,
sender
));
throwable.printStackTrace();
}
}
};
}
private MutableText decorateHoverStacktrace(final MutableText input, final Throwable cause, final C sender) {
if (!this.manager.hasPermission(sender, "cloud.hover-stacktrace")) {
return input;
}
final StringWriter writer = new StringWriter();
cause.printStackTrace(new PrintWriter(writer));
final String stackTrace = writer.toString().replace("\t", " ");
return input.styled(style -> style
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new LiteralText(stackTrace)
.append(NEWLINE)
.append(new LiteralText(" Click to copy")
.styled(s2 -> s2
.withColor(Formatting.GRAY)
.withItalic(true)))
))
.withClickEvent(new ClickEvent(
ClickEvent.Action.COPY_TO_CLIPBOARD,
stackTrace
)));
}
}

View file

@ -0,0 +1,28 @@
//
// 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.
//
/**
* Fabric API-based implementation of Cloud
*/
package cloud.commandframework.fabric;

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -0,0 +1,21 @@
{
"schemaVersion": 1,
"id": "cloud",
"version": "${version}",
"name": "Cloud",
"description": "Command framework and dispatcher for the JVM",
"authors": [ "Alexander Söderberg" ],
"contact": {
"homepage": "https://commandframework.cloud/",
"sources": "https://github.com/Incendo/cloud"
},
"license": "MIT",
"icon": "assets/cloud/logo.png",
"depends": {
"fabricloader": ">=0.7.4",
"fabric-command-api-v1": "*",
"minecraft": ">=1.14"
}
}

View file

@ -0,0 +1,68 @@
//
// 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.testmod;
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 net.fabricmc.api.ModInitializer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.LiteralText;
import net.minecraft.text.TextColor;
public final class FabricExample implements ModInitializer {
private static final CommandArgument<ServerCommandSource, String> NAME = StringArgument.of("name");
private static final CommandArgument<ServerCommandSource, Integer> HUGS = IntegerArgument.<ServerCommandSource>newBuilder("hugs")
.asOptionalWithDefault("1")
.build();
@Override
public void onInitialize() {
// Create a commands manager. We'll use native command source types for this.
final FabricCommandManager<ServerCommandSource> manager =
FabricCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator());
manager.command(manager.commandBuilder("cloudtest")
.argument(NAME)
.argument(HUGS)
.handler(ctx -> {
ctx.getSender().sendFeedback(new LiteralText("Hello, ")
.append(ctx.get(NAME))
.append(", hope you're doing well!")
.styled(style -> style.withColor(TextColor.fromRgb(0xAA22BB))), false);
ctx.getSender().sendFeedback(new LiteralText("Cloud would like to give you ")
.append(new LiteralText(String.valueOf(ctx.get(HUGS)))
.styled(style -> style.withColor(TextColor.fromRgb(0xFAB3DA))))
.append(" hug(s) <3")
.styled(style -> style.withBold(true)), false);
}));
}
}

View file

@ -0,0 +1,28 @@
//
// 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.
//
/**
* A test mod for the fabric implementation of Cloud.
*/
package cloud.commandframework.fabric.testmod;

View file

@ -0,0 +1,28 @@
{
"schemaVersion": 1,
"id": "cloud-testmod",
"version": "${version}",
"name": "Cloud Test mod",
"description": "Command framework and dispatcher for the JVM",
"authors": [ "Alexander Söderberg" ],
"contact": {
"homepage": "https://commandframework.cloud/",
"sources": "https://github.com/Incendo/cloud"
},
"license": "MIT",
"icon": "assets/cloud/logo.png",
"entrypoints": {
"main": [
"cloud.commandframework.fabric.testmod.FabricExample"
]
},
"depends": {
"fabricloader": ">=0.7.4",
"fabric-command-api-v1": "*",
"minecraft": ">=1.14",
"cloud": "*"
}
}