diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java index d7283d87..1382d5ef 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java @@ -28,8 +28,11 @@ import cloud.commandframework.CommandTree; import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator; import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.fabric.argument.FabricArgumentParsers; -import cloud.commandframework.fabric.argument.server.MessageArgument; import cloud.commandframework.fabric.data.Message; +import cloud.commandframework.fabric.data.MultipleEntitySelector; +import cloud.commandframework.fabric.data.MultiplePlayerSelector; +import cloud.commandframework.fabric.data.SingleEntitySelector; +import cloud.commandframework.fabric.data.SinglePlayerSelector; import cloud.commandframework.meta.CommandMeta; import io.leangen.geantyref.TypeToken; import me.lucko.fabric.api.permissions.v0.Permissions; @@ -121,6 +124,24 @@ public final class FabricServerCommandManager extends FabricCommandManager FabricArgumentParsers.message()); + + // Entity selectors + this.getParserRegistry().registerParserSupplier( + TypeToken.get(SinglePlayerSelector.class), + params -> FabricArgumentParsers.singlePlayerSelector() + ); + this.getParserRegistry().registerParserSupplier( + TypeToken.get(MultiplePlayerSelector.class), + params -> FabricArgumentParsers.multiplePlayerSelector() + ); + this.getParserRegistry().registerParserSupplier( + TypeToken.get(SingleEntitySelector.class), + params -> FabricArgumentParsers.singleEntitySelector() + ); + this.getParserRegistry().registerParserSupplier( + TypeToken.get(MultipleEntitySelector.class), + params -> FabricArgumentParsers.multipleEntitySelector() + ); } /** diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/FabricArgumentParsers.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/FabricArgumentParsers.java index 6981fbba..17fba4fc 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/FabricArgumentParsers.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/FabricArgumentParsers.java @@ -28,20 +28,25 @@ import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.brigadier.argument.WrappedBrigadierParser; import cloud.commandframework.fabric.FabricCommandContextKeys; -import cloud.commandframework.fabric.argument.server.MessageArgument; import cloud.commandframework.fabric.data.Message; import cloud.commandframework.fabric.data.MinecraftTime; +import cloud.commandframework.fabric.data.MultipleEntitySelector; +import cloud.commandframework.fabric.data.MultiplePlayerSelector; +import cloud.commandframework.fabric.data.SingleEntitySelector; +import cloud.commandframework.fabric.data.SinglePlayerSelector; +import cloud.commandframework.fabric.internal.EntitySelectorAccess; import cloud.commandframework.fabric.mixin.MessageArgumentTypeMessageFormatAccess; import cloud.commandframework.fabric.mixin.MessageArgumentTypeMessageSelectorAccess; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.command.CommandSource; -import net.minecraft.command.argument.FunctionArgumentType; +import net.minecraft.command.EntitySelector; +import net.minecraft.command.argument.EntityArgumentType; import net.minecraft.command.argument.MessageArgumentType; import net.minecraft.command.argument.TimeArgumentType; import net.minecraft.entity.Entity; import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.function.CommandFunction; import net.minecraft.text.Text; +import org.checkerframework.checker.nullness.qual.NonNull; import java.util.Collection; import java.util.Collections; @@ -79,12 +84,88 @@ public final class FabricArgumentParsers { } */ + public static ArgumentParser singlePlayerSelector() { + return new WrappedBrigadierParser(EntityArgumentType.player()) + .map((ctx, entitySelector) -> { + final CommandSource either = ctx.get(FabricCommandContextKeys.NATIVE_COMMAND_SOURCE); + if (!(either instanceof ServerCommandSource)) { + return ArgumentParseResult.failure(serverOnly()); + } + try { + return ArgumentParseResult.success(new SinglePlayerSelector( + ((EntitySelectorAccess) entitySelector).inputString(), + entitySelector, + entitySelector.getPlayer((ServerCommandSource) either) + )); + } catch (final CommandSyntaxException ex) { + return ArgumentParseResult.failure(ex); + } + }); + } + + public static ArgumentParser multiplePlayerSelector() { + return new WrappedBrigadierParser(EntityArgumentType.players()) + .map((ctx, entitySelector) -> { + final CommandSource either = ctx.get(FabricCommandContextKeys.NATIVE_COMMAND_SOURCE); + if (!(either instanceof ServerCommandSource)) { + return ArgumentParseResult.failure(serverOnly()); + } + try { + return ArgumentParseResult.success(new MultiplePlayerSelector( + ((EntitySelectorAccess) entitySelector).inputString(), + entitySelector, + entitySelector.getPlayers((ServerCommandSource) either) + )); + } catch (final CommandSyntaxException ex) { + return ArgumentParseResult.failure(ex); + } + }); + } + + public static ArgumentParser singleEntitySelector() { + return new WrappedBrigadierParser(EntityArgumentType.entity()) + .map((ctx, entitySelector) -> { + final CommandSource either = ctx.get(FabricCommandContextKeys.NATIVE_COMMAND_SOURCE); + if (!(either instanceof ServerCommandSource)) { + return ArgumentParseResult.failure(serverOnly()); + } + try { + return ArgumentParseResult.success(new SingleEntitySelector( + ((EntitySelectorAccess) entitySelector).inputString(), + entitySelector, + entitySelector.getEntity((ServerCommandSource) either) + )); + } catch (final CommandSyntaxException ex) { + return ArgumentParseResult.failure(ex); + } + }); + } + + public static ArgumentParser multipleEntitySelector() { + return new WrappedBrigadierParser(EntityArgumentType.entities()) + .map((ctx, entitySelector) -> { + final CommandSource either = ctx.get(FabricCommandContextKeys.NATIVE_COMMAND_SOURCE); + if (!(either instanceof ServerCommandSource)) { + return ArgumentParseResult.failure(serverOnly()); + } + try { + return ArgumentParseResult.success(new MultipleEntitySelector( + ((EntitySelectorAccess) entitySelector).inputString(), + entitySelector, + Collections.unmodifiableCollection(entitySelector.getEntities((ServerCommandSource) either)) + )); + } catch (final CommandSyntaxException ex) { + return ArgumentParseResult.failure(ex); + } + }); + } + public static ArgumentParser message() { return new WrappedBrigadierParser(MessageArgumentType.message()) .map((ctx, format) -> { final CommandSource either = ctx.get(FabricCommandContextKeys.NATIVE_COMMAND_SOURCE); if (!(either instanceof ServerCommandSource)) { - return ArgumentParseResult.failure(new IllegalStateException("This argument is server-only")); + return ArgumentParseResult.failure(serverOnly()); } try { return ArgumentParseResult.success(MessageImpl.from( @@ -98,6 +179,10 @@ public final class FabricArgumentParsers { }); } + private static @NonNull IllegalStateException serverOnly() { + return new IllegalStateException("This argument is server-only"); + } + static final class MessageImpl implements Message { private final Collection mentionedEntities; private final Text contents; diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/MultipleEntitySelectorArgument.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/MultipleEntitySelectorArgument.java new file mode 100644 index 00000000..644ce1e3 --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/MultipleEntitySelectorArgument.java @@ -0,0 +1,110 @@ +// +// 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.argument; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.fabric.data.MultipleEntitySelector; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.function.BiFunction; + +/** + * An argument for selecting multiple entities + * + * @param the sender type + * @since 1.5.0 + */ +public final class MultipleEntitySelectorArgument extends CommandArgument { + + MultipleEntitySelectorArgument( + final boolean required, + final @NonNull String name, + final @NonNull String defaultValue, + final @Nullable BiFunction, String, List> suggestionsProvider + ) { + super( + required, + name, + FabricArgumentParsers.multipleEntitySelector(), + defaultValue, + MultipleEntitySelector.class, + suggestionsProvider + ); + } + + /** + * Create a new builder. + * + * @param name Name of the argument + * @param Command sender type + * @return Created builder + */ + public static MultipleEntitySelectorArgument.@NonNull Builder newBuilder(final @NonNull String name) { + return new MultipleEntitySelectorArgument.Builder<>(name); + } + + /** + * Create a new required command argument. + * + * @param name Component name + * @param Command sender type + * @return Created argument + */ + public static @NonNull MultipleEntitySelectorArgument of(final @NonNull String name) { + return MultipleEntitySelectorArgument.newBuilder(name).asRequired().build(); + } + + /** + * Create a new optional command argument + * + * @param name Component name + * @param Command sender type + * @return Created argument + */ + public static @NonNull MultipleEntitySelectorArgument optional(final @NonNull String name) { + return MultipleEntitySelectorArgument.newBuilder(name).asOptional().build(); + } + + public static final class Builder extends TypedBuilder> { + + Builder(final @NonNull String name) { + super(MultipleEntitySelector.class, name); + } + + /** + * Build a multiple entity selector argument + * + * @return Constructed argument + */ + @Override + public @NonNull MultipleEntitySelectorArgument build() { + return new MultipleEntitySelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider()); + } + + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/MultiplePlayerSelectorArgument.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/MultiplePlayerSelectorArgument.java new file mode 100644 index 00000000..281db858 --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/MultiplePlayerSelectorArgument.java @@ -0,0 +1,110 @@ +// +// 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.argument; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.fabric.data.MultiplePlayerSelector; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.function.BiFunction; + +/** + * An argument for selecting multiple players + * + * @param the sender type + * @since 1.5.0 + */ +public final class MultiplePlayerSelectorArgument extends CommandArgument { + + MultiplePlayerSelectorArgument( + final boolean required, + final @NonNull String name, + final @NonNull String defaultValue, + final @Nullable BiFunction, String, List> suggestionsProvider + ) { + super( + required, + name, + FabricArgumentParsers.multiplePlayerSelector(), + defaultValue, + MultiplePlayerSelector.class, + suggestionsProvider + ); + } + + /** + * Create a new builder. + * + * @param name Name of the argument + * @param Command sender type + * @return Created builder + */ + public static MultiplePlayerSelectorArgument.@NonNull Builder newBuilder(final @NonNull String name) { + return new MultiplePlayerSelectorArgument.Builder<>(name); + } + + /** + * Create a new required command argument. + * + * @param name Component name + * @param Command sender type + * @return Created argument + */ + public static @NonNull MultiplePlayerSelectorArgument of(final @NonNull String name) { + return MultiplePlayerSelectorArgument.newBuilder(name).asRequired().build(); + } + + /** + * Create a new optional command argument + * + * @param name Component name + * @param Command sender type + * @return Created argument + */ + public static @NonNull MultiplePlayerSelectorArgument optional(final @NonNull String name) { + return MultiplePlayerSelectorArgument.newBuilder(name).asOptional().build(); + } + + public static final class Builder extends TypedBuilder> { + + Builder(final @NonNull String name) { + super(MultiplePlayerSelector.class, name); + } + + /** + * Build a multiple player selector argument + * + * @return Constructed argument + */ + @Override + public @NonNull MultiplePlayerSelectorArgument build() { + return new MultiplePlayerSelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider()); + } + + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SingleEntitySelectorArgument.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SingleEntitySelectorArgument.java new file mode 100644 index 00000000..fe23093c --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SingleEntitySelectorArgument.java @@ -0,0 +1,110 @@ +// +// 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.argument; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.fabric.data.SingleEntitySelector; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.function.BiFunction; + +/** + * An argument for selecting a single entity + * + * @param the sender type + * @since 1.5.0 + */ +public final class SingleEntitySelectorArgument extends CommandArgument { + + SingleEntitySelectorArgument( + final boolean required, + final @NonNull String name, + final @NonNull String defaultValue, + final @Nullable BiFunction, String, List> suggestionsProvider + ) { + super( + required, + name, + FabricArgumentParsers.singleEntitySelector(), + defaultValue, + SingleEntitySelector.class, + suggestionsProvider + ); + } + + /** + * Create a new builder. + * + * @param name Name of the argument + * @param Command sender type + * @return Created builder + */ + public static SingleEntitySelectorArgument.@NonNull Builder newBuilder(final @NonNull String name) { + return new SingleEntitySelectorArgument.Builder<>(name); + } + + /** + * Create a new required command argument. + * + * @param name Component name + * @param Command sender type + * @return Created argument + */ + public static @NonNull SingleEntitySelectorArgument of(final @NonNull String name) { + return SingleEntitySelectorArgument.newBuilder(name).asRequired().build(); + } + + /** + * Create a new optional command argument + * + * @param name Component name + * @param Command sender type + * @return Created argument + */ + public static @NonNull SingleEntitySelectorArgument optional(final @NonNull String name) { + return SingleEntitySelectorArgument.newBuilder(name).asOptional().build(); + } + + public static final class Builder extends TypedBuilder> { + + Builder(final @NonNull String name) { + super(SingleEntitySelector.class, name); + } + + /** + * Build a single entity selector argument + * + * @return Constructed argument + */ + @Override + public @NonNull SingleEntitySelectorArgument build() { + return new SingleEntitySelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider()); + } + + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SinglePlayerSelectorArgument.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SinglePlayerSelectorArgument.java new file mode 100644 index 00000000..67c9776a --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SinglePlayerSelectorArgument.java @@ -0,0 +1,110 @@ +// +// 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.argument; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.fabric.data.SinglePlayerSelector; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.function.BiFunction; + +/** + * An argument for selecting a single player + * + * @param the sender type + * @since 1.5.0 + */ +public final class SinglePlayerSelectorArgument extends CommandArgument { + + SinglePlayerSelectorArgument( + final boolean required, + final @NonNull String name, + final @NonNull String defaultValue, + final @Nullable BiFunction, String, List> suggestionsProvider + ) { + super( + required, + name, + FabricArgumentParsers.singlePlayerSelector(), + defaultValue, + SinglePlayerSelector.class, + suggestionsProvider + ); + } + + /** + * Create a new builder. + * + * @param name Name of the argument + * @param Command sender type + * @return Created builder + */ + public static SinglePlayerSelectorArgument.@NonNull Builder newBuilder(final @NonNull String name) { + return new SinglePlayerSelectorArgument.Builder<>(name); + } + + /** + * Create a new required command argument. + * + * @param name Component name + * @param Command sender type + * @return Created argument + */ + public static @NonNull SinglePlayerSelectorArgument of(final @NonNull String name) { + return SinglePlayerSelectorArgument.newBuilder(name).asRequired().build(); + } + + /** + * Create a new optional command argument + * + * @param name Component name + * @param Command sender type + * @return Created argument + */ + public static @NonNull SinglePlayerSelectorArgument optional(final @NonNull String name) { + return SinglePlayerSelectorArgument.newBuilder(name).asOptional().build(); + } + + public static final class Builder extends TypedBuilder> { + + Builder(final @NonNull String name) { + super(SinglePlayerSelector.class, name); + } + + /** + * Build a single player selector argument + * + * @return Constructed argument + */ + @Override + public @NonNull SinglePlayerSelectorArgument build() { + return new SinglePlayerSelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider()); + } + + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/MultipleEntitySelector.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/MultipleEntitySelector.java new file mode 100644 index 00000000..8040a3c0 --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/MultipleEntitySelector.java @@ -0,0 +1,63 @@ +// +// 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.data; + +import net.minecraft.command.EntitySelector; +import net.minecraft.entity.Entity; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Collection; + +public final class MultipleEntitySelector implements Selector { + + private final String inputString; + private final net.minecraft.command.EntitySelector entitySelector; + private final Collection selectedEntities; + + public MultipleEntitySelector( + final @NonNull String inputString, + final @NonNull EntitySelector entitySelector, + final @NonNull Collection selectedEntities + ) { + this.inputString = inputString; + this.entitySelector = entitySelector; + this.selectedEntities = selectedEntities; + } + + @Override + public @NonNull String getInput() { + return this.inputString; + } + + @Override + public @NonNull EntitySelector getSelector() { + return this.entitySelector; + } + + @Override + public @NonNull Collection get() { + return this.selectedEntities; + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/MultiplePlayerSelector.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/MultiplePlayerSelector.java new file mode 100644 index 00000000..c2b53cd1 --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/MultiplePlayerSelector.java @@ -0,0 +1,63 @@ +// +// 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.data; + +import net.minecraft.command.EntitySelector; +import net.minecraft.server.network.ServerPlayerEntity; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Collection; + +public final class MultiplePlayerSelector implements Selector { + + private final String inputString; + private final EntitySelector entitySelector; + private final Collection selectedPlayers; + + public MultiplePlayerSelector( + final @NonNull String inputString, + final @NonNull EntitySelector entitySelector, + final @NonNull Collection selectedPlayers + ) { + this.inputString = inputString; + this.entitySelector = entitySelector; + this.selectedPlayers = selectedPlayers; + } + + @Override + public @NonNull String getInput() { + return this.inputString; + } + + @Override + public @NonNull EntitySelector getSelector() { + return this.entitySelector; + } + + @Override + public @NonNull Collection get() { + return this.selectedPlayers; + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/SingleEntitySelector.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/SingleEntitySelector.java new file mode 100644 index 00000000..97904d0b --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/SingleEntitySelector.java @@ -0,0 +1,61 @@ +// +// 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.data; + +import net.minecraft.command.EntitySelector; +import net.minecraft.entity.Entity; +import org.checkerframework.checker.nullness.qual.NonNull; + +public final class SingleEntitySelector implements Selector.Single { + + private final String inputString; + private final EntitySelector entitySelector; + private final Entity selectedEntity; + + public SingleEntitySelector( + final @NonNull String inputString, + final @NonNull EntitySelector entitySelector, + final @NonNull Entity selectedEntity + ) { + this.inputString = inputString; + this.entitySelector = entitySelector; + this.selectedEntity = selectedEntity; + } + + @Override + public @NonNull String getInput() { + return this.inputString; + } + + @Override + public @NonNull EntitySelector getSelector() { + return this.entitySelector; + } + + @Override + public @NonNull Entity getSingle() { + return this.selectedEntity; + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/SinglePlayerSelector.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/SinglePlayerSelector.java new file mode 100644 index 00000000..6a1be34c --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/data/SinglePlayerSelector.java @@ -0,0 +1,61 @@ +// +// 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.data; + +import net.minecraft.command.EntitySelector; +import net.minecraft.server.network.ServerPlayerEntity; +import org.checkerframework.checker.nullness.qual.NonNull; + +public final class SinglePlayerSelector implements Selector.Single { + + private final String inputString; + private final EntitySelector entitySelector; + private final ServerPlayerEntity selectedPlayer; + + public SinglePlayerSelector( + final @NonNull String inputString, + final @NonNull EntitySelector entitySelector, + final @NonNull ServerPlayerEntity selectedPlayer + ) { + this.inputString = inputString; + this.entitySelector = entitySelector; + this.selectedPlayer = selectedPlayer; + } + + @Override + public @NonNull String getInput() { + return this.inputString; + } + + @Override + public @NonNull EntitySelector getSelector() { + return this.entitySelector; + } + + @Override + public @NonNull ServerPlayerEntity getSingle() { + return this.selectedPlayer; + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/internal/EntitySelectorAccess.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/internal/EntitySelectorAccess.java new file mode 100644 index 00000000..2b174ab3 --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/internal/EntitySelectorAccess.java @@ -0,0 +1,34 @@ +// +// 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.internal; + +import org.checkerframework.checker.nullness.qual.NonNull; + +public interface EntitySelectorAccess { + + @NonNull String inputString(); + + void inputString(@NonNull String inputString); + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/CloudStringReaderMixin.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/CloudStringReaderMixin.java index fa0c0bc2..488f1004 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/CloudStringReaderMixin.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/CloudStringReaderMixin.java @@ -45,7 +45,7 @@ public class CloudStringReaderMixin implements StringReaderAsQueue { 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) { + private 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; diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/EntitySelectorMixin.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/EntitySelectorMixin.java new file mode 100644 index 00000000..5cde1492 --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/EntitySelectorMixin.java @@ -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.mixin; + +import cloud.commandframework.fabric.internal.EntitySelectorAccess; +import net.minecraft.command.EntitySelector; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(EntitySelector.class) +@Implements({@Interface(iface = EntitySelectorAccess.class, prefix = "cloud$", unique = true)}) +abstract class EntitySelectorMixin { + + @Unique + private String inputString; + + public @NonNull String cloud$inputString() { + return this.inputString; + } + + public void cloud$inputString(final @NonNull String inputString) { + this.inputString = inputString; + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/EntitySelectorReaderMixin.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/EntitySelectorReaderMixin.java new file mode 100644 index 00000000..f1d9d029 --- /dev/null +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/mixin/EntitySelectorReaderMixin.java @@ -0,0 +1,57 @@ +// +// 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.mixin; + +import cloud.commandframework.fabric.internal.EntitySelectorAccess; +import com.mojang.brigadier.StringReader; +import net.minecraft.command.EntitySelector; +import net.minecraft.command.EntitySelectorReader; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(EntitySelectorReader.class) +abstract class EntitySelectorReaderMixin { + + @Shadow + private int startCursor; + + @Shadow + @Final + private StringReader reader; + + @Unique + @Inject(method = "read", at = @At("RETURN")) + public void setInputString(final @NonNull CallbackInfoReturnable cir) { + final EntitySelector selector = cir.getReturnValue(); + final String inputString = this.reader.getString().substring(this.startCursor, this.reader.getCursor()); + ((EntitySelectorAccess) selector).inputString(inputString); + } + +} diff --git a/cloud-minecraft/cloud-fabric/src/main/resources/cloud.mixins.json b/cloud-minecraft/cloud-fabric/src/main/resources/cloud.mixins.json index 529fd755..d543f171 100644 --- a/cloud-minecraft/cloud-fabric/src/main/resources/cloud.mixins.json +++ b/cloud-minecraft/cloud-fabric/src/main/resources/cloud.mixins.json @@ -5,6 +5,8 @@ "mixins": [ "CloudStringReaderMixin", "CommandManagerMixin", + "EntitySelectorMixin", + "EntitySelectorReaderMixin", "MessageArgumentTypeMessageFormatAccess", "MessageArgumentTypeMessageSelectorAccess" ], diff --git a/cloud-minecraft/cloud-fabric/src/main/resources/fabric.mod.json b/cloud-minecraft/cloud-fabric/src/main/resources/fabric.mod.json index 5c5dd683..e23fea39 100644 --- a/cloud-minecraft/cloud-fabric/src/main/resources/fabric.mod.json +++ b/cloud-minecraft/cloud-fabric/src/main/resources/fabric.mod.json @@ -13,6 +13,10 @@ "license": "MIT", "icon": "assets/cloud/logo.png", + "mixins": [ + "cloud.mixins.json" + ], + "depends": { "fabricloader": ">=0.7.4", "fabric-command-api-v1": "*", diff --git a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricClientExample.java b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricClientExample.java index 8c01e3a4..a71a85b0 100644 --- a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricClientExample.java +++ b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricClientExample.java @@ -28,7 +28,7 @@ import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.fabric.FabricClientCommandManager; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; -import net.minecraft.text.Text; +import net.minecraft.text.LiteralText; public class FabricClientExample implements ClientModInitializer { @Override @@ -41,7 +41,7 @@ public class FabricClientExample implements ClientModInitializer { .literal("say") .argument(StringArgument.greedy("message")) .handler(ctx -> ctx.getSender().sendFeedback( - Text.of("Cloud client commands says: " + ctx.get("message")) + new LiteralText("Cloud client commands says: " + ctx.get("message")) )) ); } diff --git a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java index 8b03c35d..68b34997 100644 --- a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java +++ b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java @@ -30,6 +30,8 @@ import cloud.commandframework.arguments.standard.IntegerArgument; import cloud.commandframework.arguments.standard.StringArgument; import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.fabric.FabricServerCommandManager; +import cloud.commandframework.fabric.argument.MultiplePlayerSelectorArgument; +import cloud.commandframework.fabric.data.MultiplePlayerSelector; import cloud.commandframework.meta.CommandMeta; import com.google.gson.JsonObject; import com.google.gson.internal.Streams; @@ -40,23 +42,29 @@ 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.network.MessageType; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.ClickEvent; import net.minecraft.text.LiteralText; import net.minecraft.text.TextColor; +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; public final class FabricExample implements ModInitializer { private static final CommandArgument NAME = StringArgument.of("name"); private static final CommandArgument HUGS = IntegerArgument.newBuilder("hugs") .asOptionalWithDefault("1") .build(); + private static final CommandArgument PLAYER_SELECTOR = + MultiplePlayerSelectorArgument.of("players"); @Override public void onInitialize() { @@ -112,7 +120,17 @@ public final class FabricExample implements ModInitializer { } })); - + manager.command(base.literal("wave") + .argument(PLAYER_SELECTOR) + .handler(ctx -> { + final MultiplePlayerSelector selector = ctx.get(PLAYER_SELECTOR); + final Collection selected = selector.get(); + selected.forEach(selectedPlayer -> + selectedPlayer.sendMessage(new LiteralText("Wave"), MessageType.SYSTEM, Util.NIL_UUID)); + ctx.getSender().sendFeedback(new LiteralText(String.format("Waved at %d players (%s)", selected.size(), + selector.getInput())), + false); + })); }