fabric: Implement entity/player selector argument types

This commit is contained in:
jmp 2021-03-10 19:36:32 -08:00 committed by Jason
parent 2659eac93b
commit 78e7146ba3
18 changed files with 967 additions and 9 deletions

View file

@ -28,8 +28,11 @@ import cloud.commandframework.CommandTree;
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator; import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator;
import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.fabric.argument.FabricArgumentParsers; import cloud.commandframework.fabric.argument.FabricArgumentParsers;
import cloud.commandframework.fabric.argument.server.MessageArgument;
import cloud.commandframework.fabric.data.Message; 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 cloud.commandframework.meta.CommandMeta;
import io.leangen.geantyref.TypeToken; import io.leangen.geantyref.TypeToken;
import me.lucko.fabric.api.permissions.v0.Permissions; import me.lucko.fabric.api.permissions.v0.Permissions;
@ -121,6 +124,24 @@ public final class FabricServerCommandManager<C> extends FabricCommandManager<C,
private void registerParsers() { private void registerParsers() {
this.getParserRegistry().registerParserSupplier(TypeToken.get(Message.class), params -> FabricArgumentParsers.message()); this.getParserRegistry().registerParserSupplier(TypeToken.get(Message.class), params -> 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()
);
} }
/** /**

View file

@ -28,20 +28,25 @@ import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser; import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
import cloud.commandframework.fabric.FabricCommandContextKeys; import cloud.commandframework.fabric.FabricCommandContextKeys;
import cloud.commandframework.fabric.argument.server.MessageArgument;
import cloud.commandframework.fabric.data.Message; import cloud.commandframework.fabric.data.Message;
import cloud.commandframework.fabric.data.MinecraftTime; 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.MessageArgumentTypeMessageFormatAccess;
import cloud.commandframework.fabric.mixin.MessageArgumentTypeMessageSelectorAccess; import cloud.commandframework.fabric.mixin.MessageArgumentTypeMessageSelectorAccess;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.CommandSource; 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.MessageArgumentType;
import net.minecraft.command.argument.TimeArgumentType; import net.minecraft.command.argument.TimeArgumentType;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.function.CommandFunction;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -79,12 +84,88 @@ public final class FabricArgumentParsers {
} }
*/ */
public static <C> ArgumentParser<C, SinglePlayerSelector> singlePlayerSelector() {
return new WrappedBrigadierParser<C, EntitySelector>(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 <C> ArgumentParser<C, MultiplePlayerSelector> multiplePlayerSelector() {
return new WrappedBrigadierParser<C, EntitySelector>(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 <C> ArgumentParser<C, SingleEntitySelector> singleEntitySelector() {
return new WrappedBrigadierParser<C, EntitySelector>(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 <C> ArgumentParser<C, MultipleEntitySelector> multipleEntitySelector() {
return new WrappedBrigadierParser<C, EntitySelector>(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 <C> ArgumentParser<C, Message> message() { public static <C> ArgumentParser<C, Message> message() {
return new WrappedBrigadierParser<C, MessageArgumentType.MessageFormat>(MessageArgumentType.message()) return new WrappedBrigadierParser<C, MessageArgumentType.MessageFormat>(MessageArgumentType.message())
.map((ctx, format) -> { .map((ctx, format) -> {
final CommandSource either = ctx.get(FabricCommandContextKeys.NATIVE_COMMAND_SOURCE); final CommandSource either = ctx.get(FabricCommandContextKeys.NATIVE_COMMAND_SOURCE);
if (!(either instanceof ServerCommandSource)) { if (!(either instanceof ServerCommandSource)) {
return ArgumentParseResult.failure(new IllegalStateException("This argument is server-only")); return ArgumentParseResult.failure(serverOnly());
} }
try { try {
return ArgumentParseResult.success(MessageImpl.from( 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 { static final class MessageImpl implements Message {
private final Collection<Entity> mentionedEntities; private final Collection<Entity> mentionedEntities;
private final Text contents; private final Text contents;

View file

@ -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 <C> the sender type
* @since 1.5.0
*/
public final class MultipleEntitySelectorArgument<C> extends CommandArgument<C, MultipleEntitySelector> {
MultipleEntitySelectorArgument(
final boolean required,
final @NonNull String name,
final @NonNull String defaultValue,
final @Nullable BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider
) {
super(
required,
name,
FabricArgumentParsers.multipleEntitySelector(),
defaultValue,
MultipleEntitySelector.class,
suggestionsProvider
);
}
/**
* Create a new builder.
*
* @param name Name of the argument
* @param <C> Command sender type
* @return Created builder
*/
public static <C> MultipleEntitySelectorArgument.@NonNull Builder<C> newBuilder(final @NonNull String name) {
return new MultipleEntitySelectorArgument.Builder<>(name);
}
/**
* Create a new required command argument.
*
* @param name Component name
* @param <C> Command sender type
* @return Created argument
*/
public static <C> @NonNull MultipleEntitySelectorArgument<C> of(final @NonNull String name) {
return MultipleEntitySelectorArgument.<C>newBuilder(name).asRequired().build();
}
/**
* Create a new optional command argument
*
* @param name Component name
* @param <C> Command sender type
* @return Created argument
*/
public static <C> @NonNull MultipleEntitySelectorArgument<C> optional(final @NonNull String name) {
return MultipleEntitySelectorArgument.<C>newBuilder(name).asOptional().build();
}
public static final class Builder<C> extends TypedBuilder<C, MultipleEntitySelector, Builder<C>> {
Builder(final @NonNull String name) {
super(MultipleEntitySelector.class, name);
}
/**
* Build a multiple entity selector argument
*
* @return Constructed argument
*/
@Override
public @NonNull MultipleEntitySelectorArgument<C> build() {
return new MultipleEntitySelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider());
}
}
}

View file

@ -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 <C> the sender type
* @since 1.5.0
*/
public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C, MultiplePlayerSelector> {
MultiplePlayerSelectorArgument(
final boolean required,
final @NonNull String name,
final @NonNull String defaultValue,
final @Nullable BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider
) {
super(
required,
name,
FabricArgumentParsers.multiplePlayerSelector(),
defaultValue,
MultiplePlayerSelector.class,
suggestionsProvider
);
}
/**
* Create a new builder.
*
* @param name Name of the argument
* @param <C> Command sender type
* @return Created builder
*/
public static <C> MultiplePlayerSelectorArgument.@NonNull Builder<C> newBuilder(final @NonNull String name) {
return new MultiplePlayerSelectorArgument.Builder<>(name);
}
/**
* Create a new required command argument.
*
* @param name Component name
* @param <C> Command sender type
* @return Created argument
*/
public static <C> @NonNull MultiplePlayerSelectorArgument<C> of(final @NonNull String name) {
return MultiplePlayerSelectorArgument.<C>newBuilder(name).asRequired().build();
}
/**
* Create a new optional command argument
*
* @param name Component name
* @param <C> Command sender type
* @return Created argument
*/
public static <C> @NonNull MultiplePlayerSelectorArgument<C> optional(final @NonNull String name) {
return MultiplePlayerSelectorArgument.<C>newBuilder(name).asOptional().build();
}
public static final class Builder<C> extends TypedBuilder<C, MultiplePlayerSelector, Builder<C>> {
Builder(final @NonNull String name) {
super(MultiplePlayerSelector.class, name);
}
/**
* Build a multiple player selector argument
*
* @return Constructed argument
*/
@Override
public @NonNull MultiplePlayerSelectorArgument<C> build() {
return new MultiplePlayerSelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider());
}
}
}

View file

@ -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 <C> the sender type
* @since 1.5.0
*/
public final class SingleEntitySelectorArgument<C> extends CommandArgument<C, SingleEntitySelector> {
SingleEntitySelectorArgument(
final boolean required,
final @NonNull String name,
final @NonNull String defaultValue,
final @Nullable BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider
) {
super(
required,
name,
FabricArgumentParsers.singleEntitySelector(),
defaultValue,
SingleEntitySelector.class,
suggestionsProvider
);
}
/**
* Create a new builder.
*
* @param name Name of the argument
* @param <C> Command sender type
* @return Created builder
*/
public static <C> SingleEntitySelectorArgument.@NonNull Builder<C> newBuilder(final @NonNull String name) {
return new SingleEntitySelectorArgument.Builder<>(name);
}
/**
* Create a new required command argument.
*
* @param name Component name
* @param <C> Command sender type
* @return Created argument
*/
public static <C> @NonNull SingleEntitySelectorArgument<C> of(final @NonNull String name) {
return SingleEntitySelectorArgument.<C>newBuilder(name).asRequired().build();
}
/**
* Create a new optional command argument
*
* @param name Component name
* @param <C> Command sender type
* @return Created argument
*/
public static <C> @NonNull SingleEntitySelectorArgument<C> optional(final @NonNull String name) {
return SingleEntitySelectorArgument.<C>newBuilder(name).asOptional().build();
}
public static final class Builder<C> extends TypedBuilder<C, SingleEntitySelector, Builder<C>> {
Builder(final @NonNull String name) {
super(SingleEntitySelector.class, name);
}
/**
* Build a single entity selector argument
*
* @return Constructed argument
*/
@Override
public @NonNull SingleEntitySelectorArgument<C> build() {
return new SingleEntitySelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider());
}
}
}

View file

@ -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 <C> the sender type
* @since 1.5.0
*/
public final class SinglePlayerSelectorArgument<C> extends CommandArgument<C, SinglePlayerSelector> {
SinglePlayerSelectorArgument(
final boolean required,
final @NonNull String name,
final @NonNull String defaultValue,
final @Nullable BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider
) {
super(
required,
name,
FabricArgumentParsers.singlePlayerSelector(),
defaultValue,
SinglePlayerSelector.class,
suggestionsProvider
);
}
/**
* Create a new builder.
*
* @param name Name of the argument
* @param <C> Command sender type
* @return Created builder
*/
public static <C> SinglePlayerSelectorArgument.@NonNull Builder<C> newBuilder(final @NonNull String name) {
return new SinglePlayerSelectorArgument.Builder<>(name);
}
/**
* Create a new required command argument.
*
* @param name Component name
* @param <C> Command sender type
* @return Created argument
*/
public static <C> @NonNull SinglePlayerSelectorArgument<C> of(final @NonNull String name) {
return SinglePlayerSelectorArgument.<C>newBuilder(name).asRequired().build();
}
/**
* Create a new optional command argument
*
* @param name Component name
* @param <C> Command sender type
* @return Created argument
*/
public static <C> @NonNull SinglePlayerSelectorArgument<C> optional(final @NonNull String name) {
return SinglePlayerSelectorArgument.<C>newBuilder(name).asOptional().build();
}
public static final class Builder<C> extends TypedBuilder<C, SinglePlayerSelector, Builder<C>> {
Builder(final @NonNull String name) {
super(SinglePlayerSelector.class, name);
}
/**
* Build a single player selector argument
*
* @return Constructed argument
*/
@Override
public @NonNull SinglePlayerSelectorArgument<C> build() {
return new SinglePlayerSelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider());
}
}
}

View file

@ -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<Entity> {
private final String inputString;
private final net.minecraft.command.EntitySelector entitySelector;
private final Collection<Entity> selectedEntities;
public MultipleEntitySelector(
final @NonNull String inputString,
final @NonNull EntitySelector entitySelector,
final @NonNull Collection<Entity> 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<Entity> get() {
return this.selectedEntities;
}
}

View file

@ -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<ServerPlayerEntity> {
private final String inputString;
private final EntitySelector entitySelector;
private final Collection<ServerPlayerEntity> selectedPlayers;
public MultiplePlayerSelector(
final @NonNull String inputString,
final @NonNull EntitySelector entitySelector,
final @NonNull Collection<ServerPlayerEntity> 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<ServerPlayerEntity> get() {
return this.selectedPlayers;
}
}

View file

@ -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<Entity> {
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;
}
}

View file

@ -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<ServerPlayerEntity> {
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;
}
}

View file

@ -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);
}

View file

@ -45,7 +45,7 @@ public class CloudStringReaderMixin implements StringReaderAsQueue {
private @Nullable String cloud$nextWord; private @Nullable String cloud$nextWord;
/* Next whitespace index starting at startIdx, or -1 if none is found */ /* 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) { for (int i = startIdx, length = input.length(); i < length; ++i) {
if (Character.isWhitespace(input.charAt(i))) { if (Character.isWhitespace(input.charAt(i))) {
return i; return i;

View file

@ -0,0 +1,49 @@
//
// MIT License
//
// Copyright (c) 2021 Alexander Söderberg & Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
package cloud.commandframework.fabric.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;
}
}

View file

@ -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<EntitySelector> cir) {
final EntitySelector selector = cir.getReturnValue();
final String inputString = this.reader.getString().substring(this.startCursor, this.reader.getCursor());
((EntitySelectorAccess) selector).inputString(inputString);
}
}

View file

@ -5,6 +5,8 @@
"mixins": [ "mixins": [
"CloudStringReaderMixin", "CloudStringReaderMixin",
"CommandManagerMixin", "CommandManagerMixin",
"EntitySelectorMixin",
"EntitySelectorReaderMixin",
"MessageArgumentTypeMessageFormatAccess", "MessageArgumentTypeMessageFormatAccess",
"MessageArgumentTypeMessageSelectorAccess" "MessageArgumentTypeMessageSelectorAccess"
], ],

View file

@ -13,6 +13,10 @@
"license": "MIT", "license": "MIT",
"icon": "assets/cloud/logo.png", "icon": "assets/cloud/logo.png",
"mixins": [
"cloud.mixins.json"
],
"depends": { "depends": {
"fabricloader": ">=0.7.4", "fabricloader": ">=0.7.4",
"fabric-command-api-v1": "*", "fabric-command-api-v1": "*",

View file

@ -28,7 +28,7 @@ import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.fabric.FabricClientCommandManager; import cloud.commandframework.fabric.FabricClientCommandManager;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource;
import net.minecraft.text.Text; import net.minecraft.text.LiteralText;
public class FabricClientExample implements ClientModInitializer { public class FabricClientExample implements ClientModInitializer {
@Override @Override
@ -41,7 +41,7 @@ public class FabricClientExample implements ClientModInitializer {
.literal("say") .literal("say")
.argument(StringArgument.greedy("message")) .argument(StringArgument.greedy("message"))
.handler(ctx -> ctx.getSender().sendFeedback( .handler(ctx -> ctx.getSender().sendFeedback(
Text.of("Cloud client commands says: " + ctx.get("message")) new LiteralText("Cloud client commands says: " + ctx.get("message"))
)) ))
); );
} }

View file

@ -30,6 +30,8 @@ import cloud.commandframework.arguments.standard.IntegerArgument;
import cloud.commandframework.arguments.standard.StringArgument; import cloud.commandframework.arguments.standard.StringArgument;
import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.fabric.FabricServerCommandManager; import cloud.commandframework.fabric.FabricServerCommandManager;
import cloud.commandframework.fabric.argument.MultiplePlayerSelectorArgument;
import cloud.commandframework.fabric.data.MultiplePlayerSelector;
import cloud.commandframework.meta.CommandMeta; import cloud.commandframework.meta.CommandMeta;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.internal.Streams; import com.google.gson.internal.Streams;
@ -40,23 +42,29 @@ import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.command.argument.ArgumentTypes; import net.minecraft.command.argument.ArgumentTypes;
import net.minecraft.network.MessageType;
import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.ClickEvent; 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 net.minecraft.util.Util;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Instant; import java.time.Instant;
import java.util.Collection;
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")
.asOptionalWithDefault("1") .asOptionalWithDefault("1")
.build(); .build();
private static final CommandArgument<ServerCommandSource, MultiplePlayerSelector> PLAYER_SELECTOR =
MultiplePlayerSelectorArgument.of("players");
@Override @Override
public void onInitialize() { 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<ServerPlayerEntity> 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);
}));
} }