fabric: Add a couple more commands to the test mod

This commit is contained in:
jmp 2021-03-11 02:19:47 -08:00 committed by Jason
parent d72558ee81
commit 5a15ca2167
5 changed files with 297 additions and 68 deletions

View file

@ -23,26 +23,106 @@
// //
package cloud.commandframework.fabric.testmod; package cloud.commandframework.fabric.testmod;
import cloud.commandframework.Command;
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.FabricClientCommandManager; import cloud.commandframework.fabric.FabricClientCommandManager;
import cloud.commandframework.meta.CommandMeta;
import com.google.gson.JsonObject;
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonWriter;
import com.mojang.brigadier.CommandDispatcher;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.SaveLevelScreen;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.realms.gui.screen.RealmsBridgeScreen;
import net.minecraft.command.CommandSource;
import net.minecraft.command.argument.ArgumentTypes;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
public final class FabricClientExample implements ClientModInitializer { public final class FabricClientExample implements ClientModInitializer {
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
final FabricClientCommandManager<FabricClientCommandSource> commandManager = final FabricClientCommandManager<FabricClientCommandSource> commandManager =
FabricClientCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator()); FabricClientCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator());
commandManager.command( final Command.Builder<FabricClientCommandSource> base = commandManager.commandBuilder("cloud_client");
commandManager.commandBuilder("cloud_client")
.literal("say") commandManager.command(base.literal("dump")
.argument(StringArgument.greedy("message")) .meta(CommandMeta.DESCRIPTION, "Dump the client's Brigadier command tree")
.handler(ctx -> ctx.getSender().sendFeedback( .handler(ctx -> {
new LiteralText("Cloud client commands says: " + ctx.get("message")) final Path target = FabricLoader.getInstance().getGameDir().resolve(
)) "cloud-dump-" + Instant.now().toString().replace(':', '-') + ".json"
); );
ctx.getSender().sendFeedback(
new LiteralText("Dumping command output to ")
.append(new LiteralText(target.toString())
.styled(s -> s.withClickEvent(new ClickEvent(
ClickEvent.Action.OPEN_FILE,
target.toAbsolutePath().toString()
))))
);
try (BufferedWriter writer = Files.newBufferedWriter(target); JsonWriter json = new JsonWriter(writer)) {
final CommandDispatcher<CommandSource> dispatcher = MinecraftClient.getInstance()
.getNetworkHandler()
.getCommandDispatcher();
final JsonObject object = ArgumentTypes.toJson(dispatcher, dispatcher.getRoot());
json.setIndent(" ");
Streams.write(object, json);
} catch (final IOException ex) {
ctx.getSender().sendError(new LiteralText(
"Unable to write file, see console for details: " + ex.getMessage()
));
}
}));
commandManager.command(base.literal("say")
.argument(StringArgument.greedy("message"))
.handler(ctx -> ctx.getSender().sendFeedback(
new LiteralText("Cloud client commands says: " + ctx.get("message"))
)));
commandManager.command(base.literal("quit")
.handler(ctx -> {
final MinecraftClient client = MinecraftClient.getInstance();
disconnectClient(client);
client.scheduleStop();
}));
commandManager.command(base.literal("disconnect")
.handler(ctx -> disconnectClient(MinecraftClient.getInstance())));
} }
private static void disconnectClient(final @NonNull MinecraftClient client) {
boolean singlePlayer = client.isInSingleplayer();
client.world.disconnect();
if (singlePlayer) {
client.disconnect(new SaveLevelScreen(new TranslatableText("menu.savingLevel")));
} else {
client.disconnect();
}
if (singlePlayer) {
client.openScreen(new TitleScreen());
} else if (client.isConnectedToRealms()) {
new RealmsBridgeScreen().switchToRealms(new TitleScreen());
} else {
client.openScreen(new MultiplayerScreen(new TitleScreen()));
}
}
} }

View file

@ -26,112 +26,197 @@ package cloud.commandframework.fabric.testmod;
import cloud.commandframework.Command; import cloud.commandframework.Command;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult;
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.FabricServerCommandManager; import cloud.commandframework.fabric.FabricServerCommandManager;
import cloud.commandframework.fabric.argument.ColorArgument;
import cloud.commandframework.fabric.argument.ItemDataArgument;
import cloud.commandframework.fabric.argument.server.MultiplePlayerSelectorArgument; import cloud.commandframework.fabric.argument.server.MultiplePlayerSelectorArgument;
import cloud.commandframework.fabric.data.MultiplePlayerSelector; import cloud.commandframework.fabric.data.MultiplePlayerSelector;
import cloud.commandframework.meta.CommandMeta; import cloud.commandframework.fabric.testmod.mixin.GiveCommandAccess;
import com.google.gson.JsonObject;
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonWriter;
import com.mojang.brigadier.CommandDispatcher;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient; import net.fabricmc.loader.api.ModContainer;
import net.minecraft.command.CommandSource; import net.fabricmc.loader.api.metadata.ModMetadata;
import net.minecraft.command.argument.ArgumentTypes; import net.fabricmc.loader.api.metadata.Person;
import net.minecraft.command.argument.ItemStackArgument;
import net.minecraft.network.MessageType; import net.minecraft.network.MessageType;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.ClickEvent; import net.minecraft.text.ClickEvent;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import net.minecraft.text.MutableText;
import net.minecraft.text.TextColor; import net.minecraft.text.TextColor;
import net.minecraft.util.Formatting;
import net.minecraft.util.Util; import net.minecraft.util.Util;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public final class FabricExample implements ModInitializer { 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();
private static final CommandArgument<ServerCommandSource, MultiplePlayerSelector> PLAYER_SELECTOR =
MultiplePlayerSelectorArgument.of("players");
@Override @Override
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 FabricServerCommandManager<ServerCommandSource> manager = final FabricServerCommandManager<ServerCommandSource> manager =
FabricServerCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator()); FabricServerCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator());
final Command.Builder<ServerCommandSource> base = manager.commandBuilder("cloudtest"); final Command.Builder<ServerCommandSource> base = manager.commandBuilder("cloudtest");
final CommandArgument<ServerCommandSource, String> name = StringArgument.of("name");
final CommandArgument<ServerCommandSource, Integer> hugs = IntegerArgument.<ServerCommandSource>newBuilder("hugs")
.asOptionalWithDefault("1")
.build();
manager.command(base manager.command(base
.argument(NAME) .literal("hugs")
.argument(HUGS) .argument(name)
.argument(hugs)
.handler(ctx -> { .handler(ctx -> {
ctx.getSender().sendFeedback(new LiteralText("Hello, ") ctx.getSender().sendFeedback(new LiteralText("Hello, ")
.append(ctx.get(NAME)) .append(ctx.get(name))
.append(", hope you're doing well!") .append(", hope you're doing well!")
.styled(style -> style.withColor(TextColor.fromRgb(0xAA22BB))), false); .styled(style -> style.withColor(TextColor.fromRgb(0xAA22BB))), false);
ctx.getSender().sendFeedback(new LiteralText("Cloud would like to give you ") ctx.getSender().sendFeedback(new LiteralText("Cloud would like to give you ")
.append(new LiteralText(String.valueOf(ctx.get(HUGS))) .append(new LiteralText(String.valueOf(ctx.get(hugs)))
.styled(style -> style.withColor(TextColor.fromRgb(0xFAB3DA)))) .styled(style -> style.withColor(TextColor.fromRgb(0xFAB3DA))))
.append(" hug(s) <3") .append(" hug(s) <3")
.styled(style -> style.withBold(true)), false); .styled(style -> style.withBold(true)), false);
})); }));
manager.command(base.literal("dump") final CommandArgument<ServerCommandSource, MultiplePlayerSelector> playerSelector =
.meta(CommandMeta.DESCRIPTION, "Dump the client's Brigadier command tree (integrated server only)") MultiplePlayerSelectorArgument.of("players");
.meta(FabricServerCommandManager.META_REGISTRATION_ENVIRONMENT, CommandManager.RegistrationEnvironment.INTEGRATED) final CommandArgument<ServerCommandSource, Formatting> textColor = ColorArgument.of("color");
.handler(ctx -> {
final Path target =
FabricLoader.getInstance().getGameDir().resolve(
"cloud-dump-"
+ Instant.now().toString().replace(':', '-')
+ ".json"
);
ctx.getSender().sendFeedback(new LiteralText("Dumping command output to ")
.append(new LiteralText(target.toString())
.styled(s -> s.withClickEvent(new ClickEvent(
ClickEvent.Action.OPEN_FILE,
target.toAbsolutePath().toString()
)))), false);
try (BufferedWriter writer = Files.newBufferedWriter(target); JsonWriter json = new JsonWriter(writer)) {
final CommandDispatcher<CommandSource> dispatcher = MinecraftClient.getInstance()
.getNetworkHandler()
.getCommandDispatcher();
final JsonObject object = ArgumentTypes.toJson(dispatcher, dispatcher.getRoot());
json.setIndent(" ");
Streams.write(object, json);
} catch (final IOException ex) {
ctx.getSender().sendError(new LiteralText("Unable to write file, see console for details: " + ex.getMessage()));
}
}));
manager.command(base.literal("wave") manager.command(base.literal("wave")
.argument(PLAYER_SELECTOR) .argument(playerSelector)
.argument(textColor)
.handler(ctx -> { .handler(ctx -> {
final MultiplePlayerSelector selector = ctx.get(PLAYER_SELECTOR); final MultiplePlayerSelector selector = ctx.get(playerSelector);
final Collection<ServerPlayerEntity> selected = selector.get(); final Collection<ServerPlayerEntity> selected = selector.get();
selected.forEach(selectedPlayer -> selected.forEach(selectedPlayer ->
selectedPlayer.sendMessage(new LiteralText("Wave"), MessageType.SYSTEM, Util.NIL_UUID)); selectedPlayer.sendMessage(
ctx.getSender().sendFeedback(new LiteralText(String.format("Waved at %d players (%s)", selected.size(), new LiteralText("Wave from ")
selector.getInput())), .styled(style -> style.withColor(ctx.get(textColor)))
false); .append(ctx.getSender().getDisplayName()),
MessageType.SYSTEM,
Util.NIL_UUID
));
ctx.getSender().sendFeedback(
new LiteralText(String.format("Waved at %d players (%s)", selected.size(),
selector.getInput()
)),
false
);
})); }));
manager.command(base.literal("give")
.permission("cloud.give")
.argument(MultiplePlayerSelectorArgument.of("targets"))
.argument(ItemDataArgument.of("item"))
.argument(IntegerArgument.<ServerCommandSource>newBuilder("amount")
.withMin(1)
.asOptionalWithDefault("1"))
.handler(ctx -> {
final ItemStackArgument item = ctx.get("item");
final MultiplePlayerSelector targets = ctx.get("targets");
final int amount = ctx.get("amount");
GiveCommandAccess.give(
ctx.getSender(),
item,
targets.get(),
amount
);
}));
final Command.Builder<ServerCommandSource> mods = base.literal("mods").permission("cloud.mods");
manager.command(mods.handler(ctx -> {
final List<ModMetadata> modList = FabricLoader.getInstance().getAllMods().stream()
.map(ModContainer::getMetadata)
.sorted(Comparator.comparing(ModMetadata::getId))
.collect(Collectors.toList());
final LiteralText text = new LiteralText("");
text.append(new LiteralText("Loaded Mods")
.styled(style -> style.withColor(Formatting.BLUE).withFormatting(Formatting.BOLD)));
text.append(new LiteralText(String.format(" (%s)\n", modList.size()))
.styled(style -> style.withColor(Formatting.GRAY).withFormatting(Formatting.ITALIC)));
for (final ModMetadata mod : modList) {
text.append(
new LiteralText("")
.styled(style -> style.withColor(Formatting.WHITE)
.withClickEvent(new ClickEvent(
ClickEvent.Action.SUGGEST_COMMAND,
String.format("/cloudtest mods %s", mod.getId())
))
.withHoverEvent(new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
new LiteralText("Click for more info")
)))
.append(new LiteralText(mod.getName()).styled(style -> style.withColor(Formatting.GREEN)))
.append(new LiteralText(String.format(" (%s) ", mod.getId()))
.styled(style -> style.withColor(Formatting.GRAY).withFormatting(Formatting.ITALIC)))
.append(new LiteralText(String.format("v%s", mod.getVersion())))
);
if (modList.indexOf(mod) != modList.size() - 1) {
text.append(new LiteralText(", ").styled(style -> style.withColor(Formatting.GRAY)));
}
}
ctx.getSender().sendFeedback(text, false);
}));
final CommandArgument<ServerCommandSource, ModMetadata> modMetadata = manager.argumentBuilder(ModMetadata.class, "mod")
.withSuggestionsProvider((ctx, input) -> FabricLoader.getInstance().getAllMods().stream()
.map(ModContainer::getMetadata)
.map(ModMetadata::getId)
.collect(Collectors.toList()))
.withParser((ctx, inputQueue) -> {
final ModMetadata meta = FabricLoader.getInstance().getModContainer(inputQueue.peek())
.map(ModContainer::getMetadata)
.orElse(null);
if (meta != null) {
inputQueue.remove();
return ArgumentParseResult.success(meta);
}
return ArgumentParseResult.failure(new IllegalArgumentException(String.format(
"No mod with id '%s'",
inputQueue.peek()
)));
})
.build();
manager.command(mods.argument(modMetadata)
.handler(ctx -> {
final ModMetadata meta = ctx.get(modMetadata);
final MutableText text = new LiteralText("")
.append(new LiteralText(meta.getName())
.styled(style -> style.withColor(Formatting.BLUE).withFormatting(Formatting.BOLD)))
.append(new LiteralText("\n modid: " + meta.getId()))
.append(new LiteralText("\n version: " + meta.getVersion()))
.append(new LiteralText("\n type: " + meta.getType()));
if (!meta.getDescription().isEmpty()) {
text.append(new LiteralText("\n description: " + meta.getDescription()));
}
if (!meta.getAuthors().isEmpty()) {
text.append(new LiteralText("\n authors: " + meta.getAuthors().stream()
.map(Person::getName)
.collect(Collectors.joining(", "))));
}
if (!meta.getLicense().isEmpty()) {
text.append(new LiteralText("\n license: " + String.join(", ", meta.getLicense())));
}
ctx.getSender().sendFeedback(
text,
false
);
}));
} }
} }

View file

@ -0,0 +1,49 @@
//
// MIT License
//
// Copyright (c) 2021 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.mixin;
import com.mojang.brigadier.Command;
import net.minecraft.command.argument.ItemStackArgument;
import net.minecraft.server.command.GiveCommand;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import java.util.Collection;
@Mixin(GiveCommand.class)
public interface GiveCommandAccess {
@Invoker("execute")
static int give(
final ServerCommandSource source,
final ItemStackArgument item,
final Collection<ServerPlayerEntity> targets,
final int count
) {
return Command.SINGLE_SUCCESS;
}
}

View file

@ -0,0 +1,11 @@
{
"package": "cloud.commandframework.fabric.testmod.mixin",
"compatibilityLevel": "JAVA_8",
"required": true,
"mixins": [
"GiveCommandAccess"
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -5,7 +5,7 @@
"name": "Cloud Test mod", "name": "Cloud Test mod",
"description": "Command framework and dispatcher for the JVM", "description": "Command framework and dispatcher for the JVM",
"authors": [ "Citomonstret", "zml" ], "authors": [ "Citymonstret", "zml" ],
"contact": { "contact": {
"homepage": "https://commandframework.cloud/", "homepage": "https://commandframework.cloud/",
"sources": "https://github.com/Incendo/cloud" "sources": "https://github.com/Incendo/cloud"
@ -22,6 +22,10 @@
] ]
}, },
"mixins": [
"cloud-testmod.mixins.json"
],
"depends": { "depends": {
"fabricloader": ">=0.7.4", "fabricloader": ">=0.7.4",
"fabric-command-api-v1": "*", "fabric-command-api-v1": "*",