001//
002// MIT License
003//
004// Copyright (c) 2021 Alexander Söderberg & Contributors
005//
006// Permission is hereby granted, free of charge, to any person obtaining a copy
007// of this software and associated documentation files (the "Software"), to deal
008// in the Software without restriction, including without limitation the rights
009// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010// copies of the Software, and to permit persons to whom the Software is
011// furnished to do so, subject to the following conditions:
012//
013// The above copyright notice and this permission notice shall be included in all
014// copies or substantial portions of the Software.
015//
016// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022// SOFTWARE.
023//
024package cloud.commandframework.velocity.arguments;
025
026import cloud.commandframework.ArgumentDescription;
027import cloud.commandframework.arguments.CommandArgument;
028import cloud.commandframework.arguments.parser.ArgumentParseResult;
029import cloud.commandframework.arguments.parser.ArgumentParser;
030import cloud.commandframework.captions.CaptionVariable;
031import cloud.commandframework.context.CommandContext;
032import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
033import cloud.commandframework.exceptions.parsing.ParserException;
034import cloud.commandframework.velocity.VelocityCaptionKeys;
035import com.velocitypowered.api.proxy.Player;
036import com.velocitypowered.api.proxy.ProxyServer;
037import io.leangen.geantyref.TypeToken;
038import org.checkerframework.checker.nullness.qual.NonNull;
039import org.checkerframework.checker.nullness.qual.Nullable;
040
041import java.util.Collection;
042import java.util.LinkedList;
043import java.util.List;
044import java.util.Queue;
045import java.util.function.BiFunction;
046import java.util.stream.Collectors;
047
048/**
049 * Argument parser for {@link Player players}
050 *
051 * @param <C> Command sender type
052 * @since 1.1.0
053 */
054public final class PlayerArgument<C> extends CommandArgument<C, Player> {
055
056    private PlayerArgument(
057            final boolean required,
058            final @NonNull String name,
059            final @Nullable BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider,
060            final @NonNull ArgumentDescription defaultDescription,
061            final @NonNull Collection<@NonNull BiFunction<@NonNull CommandContext<C>, @NonNull Queue<@NonNull String>,
062                    @NonNull ArgumentParseResult<Boolean>>> argumentPreprocessors
063    ) {
064        super(
065                required,
066                name,
067                new PlayerParser<>(),
068                "",
069                TypeToken.get(Player.class),
070                suggestionsProvider,
071                defaultDescription,
072                argumentPreprocessors
073        );
074    }
075
076    /**
077     * Create a new argument builder
078     *
079     * @param name Argument name
080     * @param <C>  Command sender type
081     * @return Constructed builder
082     */
083    public static <C> CommandArgument.@NonNull Builder<C, Player> newBuilder(
084            final @NonNull String name
085    ) {
086        return new Builder<C>(
087                name
088        ).withParser(
089                new PlayerParser<>()
090        );
091    }
092
093    /**
094     * Create a new required player argument
095     *
096     * @param name Argument name
097     * @param <C>  Command sender type
098     * @return Created argument
099     */
100    public static <C> @NonNull CommandArgument<C, Player> of(
101            final @NonNull String name
102    ) {
103        return PlayerArgument.<C>newBuilder(name).asRequired().build();
104    }
105
106    /**
107     * Create a new optional player argument
108     *
109     * @param name Argument name
110     * @param <C>  Command sender type
111     * @return Created argument
112     */
113    public static <C> @NonNull CommandArgument<C, Player> optional(
114            final @NonNull String name
115    ) {
116        return PlayerArgument.<C>newBuilder(name).asOptional().build();
117    }
118
119
120    public static final class Builder<C> extends CommandArgument.Builder<C, Player> {
121
122        private Builder(final @NonNull String name) {
123            super(TypeToken.get(Player.class), name);
124        }
125
126        @Override
127        public @NonNull CommandArgument<@NonNull C, @NonNull Player> build() {
128            return new PlayerArgument<>(
129                    this.isRequired(),
130                    this.getName(),
131                    this.getSuggestionsProvider(),
132                    this.getDefaultDescription(),
133                    new LinkedList<>()
134            );
135        }
136
137    }
138
139
140    public static final class PlayerParser<C> implements ArgumentParser<C, Player> {
141
142        @Override
143        public @NonNull ArgumentParseResult<@NonNull Player> parse(
144                final @NonNull CommandContext<@NonNull C> commandContext,
145                final @NonNull Queue<@NonNull String> inputQueue
146        ) {
147            final String input = inputQueue.peek();
148            if (input == null) {
149                return ArgumentParseResult.failure(new NoInputProvidedException(
150                        PlayerParser.class,
151                        commandContext
152                ));
153            }
154            final Player player = commandContext.<ProxyServer>get("ProxyServer").getPlayer(input).orElse(null);
155            if (player == null) {
156                return ArgumentParseResult.failure(
157                        new PlayerParseException(
158                                input,
159                                commandContext
160                        )
161                );
162            }
163            inputQueue.remove();
164            return ArgumentParseResult.success(player);
165        }
166
167        @Override
168        public @NonNull List<@NonNull String> suggestions(
169                final @NonNull CommandContext<C> commandContext,
170                final @NonNull String input
171        ) {
172            return commandContext.<ProxyServer>get("ProxyServer").getAllPlayers()
173                    .stream().map(Player::getUsername).collect(Collectors.toList());
174        }
175
176        @Override
177        public boolean isContextFree() {
178            return true;
179        }
180
181    }
182
183
184    public static final class PlayerParseException extends ParserException {
185
186        private static final long serialVersionUID = -4839583631837040297L;
187
188        private PlayerParseException(
189                final @NonNull String input,
190                final @NonNull CommandContext<?> context
191        ) {
192            super(
193                    PlayerParser.class,
194                    context,
195                    VelocityCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER,
196                    CaptionVariable.of("input", input)
197            );
198        }
199
200    }
201
202}