fabric: Begin adding support for some wrapped vanilla arguments
This commit is contained in:
parent
62caa2d641
commit
3be50956cc
13 changed files with 453 additions and 27 deletions
7
.checkstyle/checkstyle-suppressions.xml
Normal file
7
.checkstyle/checkstyle-suppressions.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE suppressions PUBLIC
|
||||||
|
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
|
||||||
|
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
|
||||||
|
<suppressions>
|
||||||
|
<suppress checks="(?:(?:Member|Method)Name|DesignForExtension|Javadoc.*)" files=".*[\\/]mixin[\\/].*"/>
|
||||||
|
</suppressions>
|
||||||
|
|
@ -78,9 +78,8 @@
|
||||||
|
|
||||||
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
|
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
|
||||||
<module name="SuppressionFilter">
|
<module name="SuppressionFilter">
|
||||||
<property name="file" value="${org.checkstyle.sun.suppressionfilter.config}"
|
<property name="file" value="${config_loc}/checkstyle-suppressions.xml"/>
|
||||||
default="checkstyle-suppressions.xml"/>
|
<property name="optional" value="false"/>
|
||||||
<property name="optional" value="true"/>
|
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
<!-- Checks that a package-info.java file exists for each package. -->
|
<!-- Checks that a package-info.java file exists for each package. -->
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
import net.ltgt.gradle.errorprone.errorprone
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("fabric-loom") version "0.5-SNAPSHOT"
|
id("fabric-loom") version "0.5-SNAPSHOT"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up a testmod source set
|
/* set up a testmod source set */
|
||||||
val testmod by sourceSets.creating {
|
val testmod by sourceSets.creating {
|
||||||
val main = sourceSets.main.get()
|
val main = sourceSets.main.get()
|
||||||
compileClasspath += main.compileClasspath
|
compileClasspath += main.compileClasspath
|
||||||
|
|
@ -18,30 +20,41 @@ val testmodJar by tasks.creating(Jar::class) {
|
||||||
|
|
||||||
loom.unmappedModCollection.from(testmodJar)
|
loom.unmappedModCollection.from(testmodJar)
|
||||||
|
|
||||||
tasks.withType(ProcessResources::class).configureEach {
|
/* end of testmod setup */
|
||||||
inputs.property("version", project.version)
|
|
||||||
filesMatching("fabric.mod.json") {
|
tasks {
|
||||||
expand("version" to project.version)
|
compileJava {
|
||||||
|
options.errorprone {
|
||||||
|
excludedPaths.set(".*[/\\\\]mixin[/\\\\].*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withType(ProcessResources::class).configureEach {
|
||||||
|
inputs.property("version", project.version)
|
||||||
|
filesMatching("fabric.mod.json") {
|
||||||
|
expand("version" to project.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withType(Javadoc::class).configureEach {
|
||||||
|
(options as? StandardJavadocDocletOptions)?.apply {
|
||||||
|
links("https://maven.fabricmc.net/docs/yarn-${Versions.fabricMc}+build.${Versions.fabricYarn}/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(Javadoc::class).configureEach {
|
|
||||||
(options as? StandardJavadocDocletOptions)?.apply {
|
|
||||||
links("https://maven.fabricmc.net/docs/yarn-${Versions.fabricMc}+build.${Versions.fabricYarn}/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
minecraft("com.mojang:minecraft:${Versions.fabricMc}")
|
minecraft("com.mojang", "minecraft", Versions.fabricMc)
|
||||||
mappings("net.fabricmc:yarn:${Versions.fabricMc}+build.${Versions.fabricYarn}:v2")
|
mappings("net.fabricmc", "yarn", "${Versions.fabricMc}+build.${Versions.fabricYarn}", classifier = "v2")
|
||||||
modImplementation("net.fabricmc:fabric-loader:${Versions.fabricLoader}")
|
modImplementation("net.fabricmc", "fabric-loader", Versions.fabricLoader)
|
||||||
modImplementation(fabricApi.module("fabric-command-api-v1", Versions.fabricApi))
|
modImplementation(fabricApi.module("fabric-command-api-v1", Versions.fabricApi))
|
||||||
|
|
||||||
api(include(project(":cloud-core"))!!)
|
api(include(project(":cloud-core"))!!)
|
||||||
implementation(include(project(":cloud-brigadier"))!!)
|
implementation(include(project(":cloud-brigadier"))!!)
|
||||||
|
|
||||||
include(project(":cloud-services"))
|
include(project(":cloud-services"))
|
||||||
include("io.leangen.geantyref:geantyref:${Versions.geantyref}")
|
include("io.leangen.geantyref", "geantyref", Versions.geantyref)
|
||||||
}
|
}
|
||||||
|
|
||||||
indra {
|
indra {
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,53 @@ package cloud.commandframework.fabric;
|
||||||
|
|
||||||
import cloud.commandframework.CommandManager;
|
import cloud.commandframework.CommandManager;
|
||||||
import cloud.commandframework.CommandTree;
|
import cloud.commandframework.CommandTree;
|
||||||
|
import cloud.commandframework.arguments.standard.UUIDArgument;
|
||||||
import cloud.commandframework.brigadier.BrigadierManagerHolder;
|
import cloud.commandframework.brigadier.BrigadierManagerHolder;
|
||||||
import cloud.commandframework.brigadier.CloudBrigadierManager;
|
import cloud.commandframework.brigadier.CloudBrigadierManager;
|
||||||
|
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator;
|
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator;
|
||||||
import cloud.commandframework.execution.CommandExecutionCoordinator;
|
import cloud.commandframework.execution.CommandExecutionCoordinator;
|
||||||
import cloud.commandframework.meta.CommandMeta;
|
import cloud.commandframework.meta.CommandMeta;
|
||||||
import cloud.commandframework.meta.SimpleCommandMeta;
|
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||||
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
|
import io.leangen.geantyref.TypeToken;
|
||||||
|
import net.minecraft.command.argument.AngleArgumentType;
|
||||||
|
import net.minecraft.command.argument.BlockPredicateArgumentType;
|
||||||
|
import net.minecraft.command.argument.ColorArgumentType;
|
||||||
|
import net.minecraft.command.argument.EntityAnchorArgumentType;
|
||||||
|
import net.minecraft.command.argument.IdentifierArgumentType;
|
||||||
|
import net.minecraft.command.argument.ItemEnchantmentArgumentType;
|
||||||
|
import net.minecraft.command.argument.ItemStackArgument;
|
||||||
|
import net.minecraft.command.argument.ItemStackArgumentType;
|
||||||
|
import net.minecraft.command.argument.MessageArgumentType;
|
||||||
|
import net.minecraft.command.argument.MobEffectArgumentType;
|
||||||
|
import net.minecraft.command.argument.NbtCompoundTagArgumentType;
|
||||||
|
import net.minecraft.command.argument.NbtPathArgumentType;
|
||||||
|
import net.minecraft.command.argument.NumberRangeArgumentType;
|
||||||
|
import net.minecraft.command.argument.ObjectiveCriteriaArgumentType;
|
||||||
|
import net.minecraft.command.argument.OperationArgumentType;
|
||||||
|
import net.minecraft.command.argument.ParticleArgumentType;
|
||||||
|
import net.minecraft.command.argument.SwizzleArgumentType;
|
||||||
|
import net.minecraft.command.argument.UuidArgumentType;
|
||||||
|
import net.minecraft.enchantment.Enchantment;
|
||||||
|
import net.minecraft.entity.effect.StatusEffect;
|
||||||
|
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.CommandManager.RegistrationEnvironment;
|
||||||
import net.minecraft.server.command.CommandOutput;
|
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.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.Vec2f;
|
||||||
import net.minecraft.util.math.Vec3d;
|
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.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class FabricCommandManager<C> extends CommandManager<C> implements BrigadierManagerHolder<C> {
|
public class FabricCommandManager<C> extends CommandManager<C> implements BrigadierManagerHolder<C> {
|
||||||
|
|
@ -114,10 +146,68 @@ public class FabricCommandManager<C> extends CommandManager<C> implements Brigad
|
||||||
)),
|
)),
|
||||||
this
|
this
|
||||||
));
|
));
|
||||||
|
this.brigadierManager.backwardsBrigadierSenderMapper(this.backwardsCommandSourceMapper);
|
||||||
|
this.registerNativeBrigadierMappings(this.brigadierManager);
|
||||||
|
|
||||||
((FabricCommandRegistrationHandler<C>) this.getCommandRegistrationHandler()).initialize(this);
|
((FabricCommandRegistrationHandler<C>) this.getCommandRegistrationHandler()).initialize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerNativeBrigadierMappings(final CloudBrigadierManager<C, ServerCommandSource> brigadier) {
|
||||||
|
/* Cloud-native argument types */
|
||||||
|
brigadier.registerMapping(new TypeToken<UUIDArgument.UUIDParser<C>>() {}, false, cloud -> UuidArgumentType.uuid());
|
||||||
|
|
||||||
|
/* Wrapped/Constant Brigadier types, native value type */
|
||||||
|
this.registerConstantNativeParserSupplier(Formatting.class, ColorArgumentType.color());
|
||||||
|
this.registerConstantNativeParserSupplier(BlockPredicateArgumentType.BlockPredicate.class,
|
||||||
|
BlockPredicateArgumentType.blockPredicate());
|
||||||
|
this.registerConstantNativeParserSupplier(MessageArgumentType.MessageFormat.class, MessageArgumentType.message());
|
||||||
|
this.registerConstantNativeParserSupplier(CompoundTag.class, NbtCompoundTagArgumentType.nbtCompound());
|
||||||
|
this.registerConstantNativeParserSupplier(NbtPathArgumentType.NbtPath.class, NbtPathArgumentType.nbtPath());
|
||||||
|
this.registerConstantNativeParserSupplier(ScoreboardCriterion.class, ObjectiveCriteriaArgumentType.objectiveCriteria());
|
||||||
|
this.registerConstantNativeParserSupplier(OperationArgumentType.Operation.class, OperationArgumentType.operation());
|
||||||
|
this.registerConstantNativeParserSupplier(ParticleEffect.class, ParticleArgumentType.particle());
|
||||||
|
this.registerConstantNativeParserSupplier(AngleArgumentType.Angle.class, AngleArgumentType.angle());
|
||||||
|
this.registerConstantNativeParserSupplier(new TypeToken<EnumSet<Direction.Axis>>() {}, SwizzleArgumentType.swizzle());
|
||||||
|
this.registerConstantNativeParserSupplier(Identifier.class, IdentifierArgumentType.identifier());
|
||||||
|
this.registerConstantNativeParserSupplier(StatusEffect.class, MobEffectArgumentType.mobEffect());
|
||||||
|
this.registerConstantNativeParserSupplier(EntityAnchorArgumentType.EntityAnchor.class, EntityAnchorArgumentType.entityAnchor());
|
||||||
|
this.registerConstantNativeParserSupplier(NumberRange.IntRange.class, NumberRangeArgumentType.numberRange());
|
||||||
|
this.registerConstantNativeParserSupplier(NumberRange.FloatRange.class, NumberRangeArgumentType.method_30918());
|
||||||
|
this.registerConstantNativeParserSupplier(Enchantment.class, ItemEnchantmentArgumentType.itemEnchantment());
|
||||||
|
// todo: can we add a compound argument -- MC `ItemStackArgument` is just type and tag, and count is separate
|
||||||
|
this.registerConstantNativeParserSupplier(ItemStackArgument.class, ItemStackArgumentType.itemStack());
|
||||||
|
|
||||||
|
/* Wrapped/Constant Brigadier types, mapped value type */
|
||||||
|
/*this.registerConstantNativeParserSupplier(GameProfile.class, GameProfileArgumentType.gameProfile());
|
||||||
|
this.registerConstantNativeParserSupplier(BlockPos.class, BlockPosArgumentType.blockPos());
|
||||||
|
this.registerConstantNativeParserSupplier(ColumnPos.class, ColumnPosArgumentType.columnPos());
|
||||||
|
this.registerConstantNativeParserSupplier(Vec3d.class, Vec3ArgumentType.vec3());
|
||||||
|
this.registerConstantNativeParserSupplier(Vec2f.class, Vec2ArgumentType.vec2());
|
||||||
|
this.registerConstantNativeParserSupplier(BlockState.class, BlockStateArgumentType.blockState());
|
||||||
|
this.registerConstantNativeParserSupplier(ItemPredicate.class, ItemPredicateArgumentType.itemPredicate());
|
||||||
|
this.registerConstantNativeParserSupplier(ScoreboardObjective.class, ObjectiveArgumentType.objective());
|
||||||
|
this.registerConstantNativeParserSupplier(PosArgument.class, RotationArgumentType.rotation()); // todo: different type
|
||||||
|
this.registerConstantNativeParserSupplier(ScoreboardSlotArgumentType.scoreboardSlot());
|
||||||
|
this.registerConstantNativeParserSupplier(Team.class, TeamArgumentType.team());
|
||||||
|
this.registerConstantNativeParserSupplier(/* slot *, ItemSlotArgumentType.itemSlot());
|
||||||
|
this.registerConstantNativeParserSupplier(CommandFunction.class, FunctionArgumentType.function());
|
||||||
|
this.registerConstantNativeParserSupplier(EntityType.class, EntitySummonArgumentType.entitySummon()); // entity summon
|
||||||
|
this.registerConstantNativeParserSupplier(ServerWorld.class, DimensionArgumentType.dimension());
|
||||||
|
this.registerConstantNativeParserSupplier(/* time representation in ticks *, TimeArgumentType.time());*/
|
||||||
|
|
||||||
|
/* Wrapped brigadier requiring parameters */
|
||||||
|
// score holder: single vs multiple
|
||||||
|
// 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) {
|
||||||
|
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.
|
* Check if a sender has a certain permission.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,17 @@ import cloud.commandframework.exceptions.NoSuchCommandException;
|
||||||
import cloud.commandframework.execution.CommandResult;
|
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 net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
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;
|
||||||
import net.minecraft.text.MutableText;
|
import net.minecraft.text.MutableText;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.text.Texts;
|
||||||
import net.minecraft.util.Formatting;
|
import net.minecraft.util.Formatting;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
@ -48,6 +52,8 @@ import java.util.concurrent.CompletionException;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger();
|
||||||
|
|
||||||
private static final Text NEWLINE = new LiteralText("\n");
|
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_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
|
||||||
private static final String MESSAGE_NO_PERMS =
|
private static final String MESSAGE_NO_PERMS =
|
||||||
|
|
@ -118,9 +124,18 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
||||||
sender,
|
sender,
|
||||||
ArgumentParseException.class,
|
ArgumentParseException.class,
|
||||||
(ArgumentParseException) throwable,
|
(ArgumentParseException) throwable,
|
||||||
(c, e) -> source.sendError(new LiteralText("Invalid Command Argument: ")
|
(c, e) -> {
|
||||||
.append(new LiteralText(finalThrowable.getCause().getMessage())
|
if (finalThrowable.getCause() instanceof CommandSyntaxException) {
|
||||||
.styled(style -> style.withColor(Formatting.GRAY))))
|
source.sendError(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: ")
|
||||||
|
.append(new LiteralText(finalThrowable.getCause().getMessage())
|
||||||
|
.formatted(Formatting.GRAY)));
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
} else if (throwable instanceof CommandExecutionException) {
|
} else if (throwable instanceof CommandExecutionException) {
|
||||||
this.manager.handleException(
|
this.manager.handleException(
|
||||||
|
|
@ -128,21 +143,21 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
||||||
CommandExecutionException.class,
|
CommandExecutionException.class,
|
||||||
(CommandExecutionException) throwable,
|
(CommandExecutionException) throwable,
|
||||||
(c, e) -> {
|
(c, e) -> {
|
||||||
source.sendError(decorateHoverStacktrace(
|
source.sendError(this.decorateHoverStacktrace(
|
||||||
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
||||||
finalThrowable.getCause(),
|
finalThrowable.getCause(),
|
||||||
sender
|
sender
|
||||||
));
|
));
|
||||||
finalThrowable.getCause().printStackTrace();
|
LOGGER.warn("Error occurred while executing command for user {}:", source.getName(), finalThrowable);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
source.sendError(decorateHoverStacktrace(
|
source.sendError(this.decorateHoverStacktrace(
|
||||||
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
||||||
throwable,
|
throwable,
|
||||||
sender
|
sender
|
||||||
));
|
));
|
||||||
throwable.printStackTrace();
|
LOGGER.warn("Error occurred while executing command for user {}:", source.getName(), throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// 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.internal;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.StringReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension to the Brigadier StringReader that also implements Queue (via mixin).
|
||||||
|
*
|
||||||
|
* @see cloud.commandframework.fabric.mixin.CloudStringReaderMixin for the {@link java.util.Queue} implementation
|
||||||
|
*/
|
||||||
|
public final class CloudStringReader extends StringReader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new reader from text.
|
||||||
|
*
|
||||||
|
* @param input the input
|
||||||
|
*/
|
||||||
|
public CloudStringReader(final String input) {
|
||||||
|
super(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation-internal content for the Fabric implementation of Cloud.
|
||||||
|
*/
|
||||||
|
package cloud.commandframework.fabric.internal;
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
//
|
||||||
|
// 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.mixin;
|
||||||
|
|
||||||
|
import cloud.commandframework.brigadier.argument.StringReaderAsQueue;
|
||||||
|
import cloud.commandframework.fabric.internal.CloudStringReader;
|
||||||
|
import com.mojang.brigadier.StringReader;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mix in to our own class in order to implement the Queue interface without signature conflicts.
|
||||||
|
*
|
||||||
|
* <p>This must be kept in sync with the wrapping implementation in {@code cloud-brigadier}</p>
|
||||||
|
*/
|
||||||
|
@Mixin(value = CloudStringReader.class, remap = false)
|
||||||
|
public class CloudStringReaderMixin implements StringReaderAsQueue {
|
||||||
|
|
||||||
|
private int cloud$nextSpaceIdx; /* the character before the start of a new word */
|
||||||
|
private @Nullable String cloud$nextWord;
|
||||||
|
|
||||||
|
/* Next whitespace index starting at startIdx, or -1 if none is found */
|
||||||
|
static int cloud$nextWhitespace(final String input, final int startIdx) {
|
||||||
|
for (int i = startIdx, length = input.length(); i < length; ++i) {
|
||||||
|
if (Character.isWhitespace(input.charAt(i))) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringReader getOriginal() {
|
||||||
|
return (StringReader) (Object) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Brigadier doesn't automatically consume whitespace... in order to get the matched behaviour, we consume whitespace
|
||||||
|
* after every popped string.
|
||||||
|
*/
|
||||||
|
private void cloud$advance() {
|
||||||
|
final int startOfNextWord = this.cloud$nextSpaceIdx + 1;
|
||||||
|
this.cloud$nextSpaceIdx = cloud$nextWhitespace(((StringReader) (Object) this).getString(), startOfNextWord);
|
||||||
|
if (this.cloud$nextSpaceIdx != -1) {
|
||||||
|
this.cloud$nextWord = ((StringReader) (Object) this).getString().substring(startOfNextWord, this.cloud$nextSpaceIdx);
|
||||||
|
} else if (startOfNextWord < ((StringReader) (Object) this).getTotalLength()) {
|
||||||
|
this.cloud$nextWord = ((StringReader) (Object) this).getString().substring(startOfNextWord);
|
||||||
|
this.cloud$nextSpaceIdx = ((StringReader) (Object) this).getTotalLength() + 1;
|
||||||
|
} else {
|
||||||
|
this.cloud$nextWord = null;
|
||||||
|
}
|
||||||
|
((StringReader) (Object) this).setCursor(startOfNextWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String poll() {
|
||||||
|
/* peek and then advance */
|
||||||
|
final String next = this.peek();
|
||||||
|
if (next != null) {
|
||||||
|
this.cloud$advance();
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String peek() {
|
||||||
|
return this.cloud$nextWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
if (this.cloud$nextWord == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int counter = 1;
|
||||||
|
for (int i = this.cloud$nextSpaceIdx;
|
||||||
|
i != -1 && i < ((StringReader) (Object) this).getTotalLength();
|
||||||
|
i = cloud$nextWhitespace(((StringReader) (Object) this).getString(), i + 1)) {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(final Object o) {
|
||||||
|
if (Objects.equals(o, this.cloud$nextWord)) {
|
||||||
|
this.cloud$advance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
StringReaderAsQueue.super.clear();
|
||||||
|
this.cloud$nextWord = null;
|
||||||
|
this.cloud$nextSpaceIdx = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// 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.mixin;
|
||||||
|
|
||||||
|
import cloud.commandframework.fabric.internal.CloudStringReader;
|
||||||
|
import com.mojang.brigadier.StringReader;
|
||||||
|
import net.minecraft.server.command.CommandManager;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin(CommandManager.class)
|
||||||
|
public class CommandManagerMixin {
|
||||||
|
|
||||||
|
/* Use our StringReader. This is technically optional, but it's nicer to avoid re-wrapping the object every time */
|
||||||
|
@Redirect(method = "execute", at = @At(value = "NEW", target = "com/mojang/brigadier/StringReader", remap = false), require = 0)
|
||||||
|
private StringReader cloud$newStringReader(final String arguments) {
|
||||||
|
return new CloudStringReader(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"package": "cloud.commandframework.fabric.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_8",
|
||||||
|
"required": true,
|
||||||
|
"mixins": [
|
||||||
|
"CloudStringReaderMixin",
|
||||||
|
"CommandManagerMixin"
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
"name": "Cloud",
|
"name": "Cloud",
|
||||||
"description": "Command framework and dispatcher for the JVM",
|
"description": "Command framework and dispatcher for the JVM",
|
||||||
"authors": [ "Alexander Söderberg" ],
|
"authors": [ "Citomonstret", "zml" ],
|
||||||
"contact": {
|
"contact": {
|
||||||
"homepage": "https://commandframework.cloud/",
|
"homepage": "https://commandframework.cloud/",
|
||||||
"sources": "https://github.com/Incendo/cloud"
|
"sources": "https://github.com/Incendo/cloud"
|
||||||
|
|
|
||||||
|
|
@ -24,16 +24,34 @@
|
||||||
|
|
||||||
package cloud.commandframework.fabric.testmod;
|
package cloud.commandframework.fabric.testmod;
|
||||||
|
|
||||||
|
import cloud.commandframework.Command;
|
||||||
import cloud.commandframework.arguments.CommandArgument;
|
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.FabricCommandManager;
|
||||||
|
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.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.command.CommandSource;
|
||||||
|
import net.minecraft.command.argument.ArgumentTypes;
|
||||||
|
import net.minecraft.server.command.CommandManager;
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import net.minecraft.text.ClickEvent;
|
||||||
import net.minecraft.text.LiteralText;
|
import net.minecraft.text.LiteralText;
|
||||||
import net.minecraft.text.TextColor;
|
import net.minecraft.text.TextColor;
|
||||||
|
|
||||||
|
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 FabricExample implements ModInitializer {
|
public final class FabricExample implements ModInitializer {
|
||||||
private static final CommandArgument<ServerCommandSource, String> NAME = StringArgument.of("name");
|
private static final CommandArgument<ServerCommandSource, String> NAME = StringArgument.of("name");
|
||||||
private static final CommandArgument<ServerCommandSource, Integer> HUGS = IntegerArgument.<ServerCommandSource>newBuilder("hugs")
|
private static final CommandArgument<ServerCommandSource, Integer> HUGS = IntegerArgument.<ServerCommandSource>newBuilder("hugs")
|
||||||
|
|
@ -47,7 +65,9 @@ public final class FabricExample implements ModInitializer {
|
||||||
final FabricCommandManager<ServerCommandSource> manager =
|
final FabricCommandManager<ServerCommandSource> manager =
|
||||||
FabricCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator());
|
FabricCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator());
|
||||||
|
|
||||||
manager.command(manager.commandBuilder("cloudtest")
|
final Command.Builder<ServerCommandSource> base = manager.commandBuilder("cloudtest");
|
||||||
|
|
||||||
|
manager.command(base
|
||||||
.argument(NAME)
|
.argument(NAME)
|
||||||
.argument(HUGS)
|
.argument(HUGS)
|
||||||
.handler(ctx -> {
|
.handler(ctx -> {
|
||||||
|
|
@ -63,6 +83,37 @@ public final class FabricExample implements ModInitializer {
|
||||||
.styled(style -> style.withBold(true)), false);
|
.styled(style -> style.withBold(true)), false);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
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)
|
||||||
|
.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()));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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": [ "Alexander Söderberg" ],
|
"authors": [ "Citomonstret", "zml" ],
|
||||||
"contact": {
|
"contact": {
|
||||||
"homepage": "https://commandframework.cloud/",
|
"homepage": "https://commandframework.cloud/",
|
||||||
"sources": "https://github.com/Incendo/cloud"
|
"sources": "https://github.com/Incendo/cloud"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue