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 -->
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${org.checkstyle.sun.suppressionfilter.config}"
|
||||
default="checkstyle-suppressions.xml"/>
|
||||
<property name="optional" value="true"/>
|
||||
<property name="file" value="${config_loc}/checkstyle-suppressions.xml"/>
|
||||
<property name="optional" value="false"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks that a package-info.java file exists for each package. -->
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import net.ltgt.gradle.errorprone.errorprone
|
||||
|
||||
plugins {
|
||||
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 main = sourceSets.main.get()
|
||||
compileClasspath += main.compileClasspath
|
||||
|
|
@ -18,30 +20,41 @@ val testmodJar by tasks.creating(Jar::class) {
|
|||
|
||||
loom.unmappedModCollection.from(testmodJar)
|
||||
|
||||
tasks.withType(ProcessResources::class).configureEach {
|
||||
/* end of testmod setup */
|
||||
|
||||
tasks {
|
||||
compileJava {
|
||||
options.errorprone {
|
||||
excludedPaths.set(".*[/\\\\]mixin[/\\\\].*")
|
||||
}
|
||||
}
|
||||
|
||||
withType(ProcessResources::class).configureEach {
|
||||
inputs.property("version", project.version)
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand("version" to project.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc::class).configureEach {
|
||||
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}")
|
||||
minecraft("com.mojang", "minecraft", Versions.fabricMc)
|
||||
mappings("net.fabricmc", "yarn", "${Versions.fabricMc}+build.${Versions.fabricYarn}", classifier = "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}")
|
||||
include("io.leangen.geantyref", "geantyref", Versions.geantyref)
|
||||
}
|
||||
|
||||
indra {
|
||||
|
|
|
|||
|
|
@ -26,21 +26,53 @@ package cloud.commandframework.fabric;
|
|||
|
||||
import cloud.commandframework.CommandManager;
|
||||
import cloud.commandframework.CommandTree;
|
||||
import cloud.commandframework.arguments.standard.UUIDArgument;
|
||||
import cloud.commandframework.brigadier.BrigadierManagerHolder;
|
||||
import cloud.commandframework.brigadier.CloudBrigadierManager;
|
||||
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
|
||||
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 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.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;
|
||||
|
||||
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.brigadierManager.backwardsBrigadierSenderMapper(this.backwardsCommandSourceMapper);
|
||||
this.registerNativeBrigadierMappings(this.brigadierManager);
|
||||
|
||||
((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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -33,13 +33,17 @@ import cloud.commandframework.exceptions.NoSuchCommandException;
|
|||
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.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.text.Texts;
|
||||
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 java.io.PrintWriter;
|
||||
|
|
@ -48,6 +52,8 @@ import java.util.concurrent.CompletionException;
|
|||
import java.util.function.BiConsumer;
|
||||
|
||||
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 String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
|
||||
private static final String MESSAGE_NO_PERMS =
|
||||
|
|
@ -118,9 +124,18 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
|||
sender,
|
||||
ArgumentParseException.class,
|
||||
(ArgumentParseException) throwable,
|
||||
(c, e) -> source.sendError(new LiteralText("Invalid Command Argument: ")
|
||||
(c, e) -> {
|
||||
if (finalThrowable.getCause() instanceof CommandSyntaxException) {
|
||||
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())
|
||||
.styled(style -> style.withColor(Formatting.GRAY))))
|
||||
.formatted(Formatting.GRAY)));
|
||||
}
|
||||
}
|
||||
);
|
||||
} else if (throwable instanceof CommandExecutionException) {
|
||||
this.manager.handleException(
|
||||
|
|
@ -128,21 +143,21 @@ final class FabricExecutor<C> implements Command<ServerCommandSource> {
|
|||
CommandExecutionException.class,
|
||||
(CommandExecutionException) throwable,
|
||||
(c, e) -> {
|
||||
source.sendError(decorateHoverStacktrace(
|
||||
source.sendError(this.decorateHoverStacktrace(
|
||||
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
||||
finalThrowable.getCause(),
|
||||
sender
|
||||
));
|
||||
finalThrowable.getCause().printStackTrace();
|
||||
LOGGER.warn("Error occurred while executing command for user {}:", source.getName(), finalThrowable);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
source.sendError(decorateHoverStacktrace(
|
||||
source.sendError(this.decorateHoverStacktrace(
|
||||
new LiteralText(MESSAGE_INTERNAL_ERROR),
|
||||
throwable,
|
||||
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",
|
||||
"description": "Command framework and dispatcher for the JVM",
|
||||
"authors": [ "Alexander Söderberg" ],
|
||||
"authors": [ "Citomonstret", "zml" ],
|
||||
"contact": {
|
||||
"homepage": "https://commandframework.cloud/",
|
||||
"sources": "https://github.com/Incendo/cloud"
|
||||
|
|
|
|||
|
|
@ -24,16 +24,34 @@
|
|||
|
||||
package cloud.commandframework.fabric.testmod;
|
||||
|
||||
import cloud.commandframework.Command;
|
||||
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.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.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.text.ClickEvent;
|
||||
import net.minecraft.text.LiteralText;
|
||||
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 {
|
||||
private static final CommandArgument<ServerCommandSource, String> NAME = StringArgument.of("name");
|
||||
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 =
|
||||
FabricCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator());
|
||||
|
||||
manager.command(manager.commandBuilder("cloudtest")
|
||||
final Command.Builder<ServerCommandSource> base = manager.commandBuilder("cloudtest");
|
||||
|
||||
manager.command(base
|
||||
.argument(NAME)
|
||||
.argument(HUGS)
|
||||
.handler(ctx -> {
|
||||
|
|
@ -63,6 +83,37 @@ public final class FabricExample implements ModInitializer {
|
|||
.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",
|
||||
"description": "Command framework and dispatcher for the JVM",
|
||||
"authors": [ "Alexander Söderberg" ],
|
||||
"authors": [ "Citomonstret", "zml" ],
|
||||
"contact": {
|
||||
"homepage": "https://commandframework.cloud/",
|
||||
"sources": "https://github.com/Incendo/cloud"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue