diff --git a/README.md b/README.md index d0d13d639..45d7571e8 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ For support, consider joining the [Legacy Fabric discord server](https://legacyf |----------------------|-------------------------------|--------|-----|-------|-------|--------|--------|--------|--------| | api-base | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | command-api-v1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| command-api-v2 | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| command-api-v2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | crash-report-info-v1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | entity-events-v1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | gamerule-api-v1 | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | diff --git a/build.gradle b/build.gradle index f678fe7ca..3234d8f0d 100644 --- a/build.gradle +++ b/build.gradle @@ -181,7 +181,8 @@ def moduleDependencies(Project project, List projectNames) { def commonProject = findProject(":${moduleName}_common") if (commonProject != null) { - depNames.add(0, commonProject.name) + def predicate = VersionHelper.parsePredicate(getMCRange(commonProject)) + if (predicate.test(projectMCVersion)) depNames.add(0, commonProject.name) } } else { depNames.removeIf { !it.endsWith("_common")} diff --git a/legacy-fabric-command-api-v2/1.6.4/build.gradle b/legacy-fabric-command-api-v2/1.6.4/build.gradle new file mode 100644 index 000000000..84db36657 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/build.gradle @@ -0,0 +1,3 @@ +loom { + accessWidenerPath = file("src/main/resources/command-api-v2.accesswidener") +} diff --git a/legacy-fabric-command-api-v2/1.6.4/gradle.properties b/legacy-fabric-command-api-v2/1.6.4/gradle.properties new file mode 100644 index 000000000..bff6c84c0 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/gradle.properties @@ -0,0 +1,2 @@ +minVersionIncluded=1.6.4 +maxVersionIncluded=1.6.4 diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/CommandRegistrar.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/CommandRegistrar.java new file mode 100644 index 000000000..c81a2e7d7 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/CommandRegistrar.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.api.command.v2; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandManager; +import net.legacyfabric.fabric.api.event.Event; +import net.legacyfabric.fabric.api.event.EventFactory; + +/** + * An entrypoint and event for registering commands to the {@link CommandManager}. + */ +@FunctionalInterface +public interface CommandRegistrar { + Event EVENT = EventFactory.createArrayBacked(CommandRegistrar.class, listeners -> (manager, dedicated) -> { + for (CommandRegistrar registrar : listeners) { + registrar.register(manager, dedicated); + } + }); + + /** + * Register your commands here. + * + * @param manager The command manager + * @param dedicated Whether the mod is running on a dedicated server + */ + void register(CommandManager manager, boolean dedicated); +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/Selector.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/Selector.java new file mode 100644 index 000000000..d17d16814 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/Selector.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.api.command.v2; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; + +import net.minecraft.command.CommandSource; +import net.minecraft.entity.Entity; +import net.minecraft.server.MinecraftServer; + +public enum Selector { + ALL_ENTITIES('e') { + @Override + public Set resolve(CommandSource sender) { + return Sets.newHashSet(sender.getWorld().entities); + } + }, + ALL_PLAYERS('a') { + @Override + public Set resolve(CommandSource sender) { + return (Set) sender.getWorld().playerEntities.stream().map(e -> (Entity) e).collect(Collectors.toSet()); + } + }, + NEAREST_PLAYER('p') { + @Override + public Set resolve(CommandSource sender) { + return Sets.newHashSet(sender.getWorld().getClosestPlayer(sender.method_4086().x, sender.method_4086().y, sender.method_4086().z, 50.0D)); + } + }, + RANDOM_PLAYER('r') { + @Override + public Set resolve(CommandSource sender) { + try { + return Sets.newHashSet((Entity) MinecraftServer.getServer().getPlayerManager().players.stream().findAny().orElseThrow(NullPointerException::new)); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + }, + EXECUTING_ENTITY('s') { + @Override + public Set resolve(CommandSource sender) { + //TODO @s didn't exist this early in the game's development, and there seems to be no code to handle it, so maybe this'll work? + return Sets.newHashSet(sender.getWorld().getPlayerByName(sender.getUsername())); + } + }; + + private final char key; + private static final Map MAP; + + Selector(char key) { + this.key = key; + } + + public char getKey() { + return this.key; + } + + public abstract Set resolve(CommandSource sender); + + public static List complete(String s) { + if (s.startsWith("@") && s.length() == 2) { + return Arrays.stream(values()).map(Selector::getKey).map(String::valueOf).distinct().collect(Collectors.toList()); + } + + return ImmutableList.of(); + } + + public static Selector parse(String value) { + if (MAP.containsKey(value)) { + return MAP.get(value); + } + + throw new IllegalArgumentException("Unknown selector"); + } + + static { + ImmutableMap.Builder builder = ImmutableMap.builder(); + + for (Selector s : values()) { + builder.put("@" + s.getKey(), s); + } + + MAP = builder.build(); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/StringType.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/StringType.java new file mode 100644 index 000000000..f294fb603 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/StringType.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.api.command.v2; + +/** + * Specifies a method to parse {@link String}s in a command. + */ +public enum StringType { + SINGLE_WORD(false), + GREEDY_PHRASE(true); + + private final boolean all; + + StringType(boolean all) { + this.all = all; + } + + public boolean isAll() { + return this.all; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandCallable.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandCallable.java new file mode 100644 index 000000000..391e7ffc8 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandCallable.java @@ -0,0 +1,128 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import java.util.List; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; +import net.minecraft.world.World; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.spec.CommandSpec; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; +import net.legacyfabric.fabric.api.util.Location; + +/** + * A low-level interface for commands that can be executed. For almost all use + * cases, higher-level tools should be used instead, like {@link CommandSpec}. + * + *

Implementations are not required to implement a sane + * {@link Object#equals(Object)} but really should.

+ */ +public interface CommandCallable { + /** + * Execute the command based on input arguments. + * + *

The implementing class must perform the necessary permission + * checks.

+ * + * @param source The caller of the command + * @param arguments The raw arguments for this command + * @return The result of a command being processed + * @throws CommandException Thrown on a command error + */ + CommandResult process(PermissibleCommandSource source, String arguments) throws CommandException; + + /** + * Gets a list of suggestions based on input. + * + *

If a suggestion is chosen by the user, it will replace the last + * word.

+ * + * @param source The command source + * @param arguments The arguments entered up to this point + * @param targetPosition The position the source is looking at when + * performing tab completion + * @return A list of suggestions + * @throws CommandException Thrown if there was a parsing error + */ + List getSuggestions(PermissibleCommandSource source, String arguments, @Nullable Location targetPosition) throws CommandException; + + /** + * Test whether this command can probably be executed by the given source. + * + *

If implementations are unsure if the command can be executed by + * the source, {@code true} should be returned. Return values of this method + * may be used to determine whether this command is listed in command + * listings.

+ * + * @param source The caller of the command + * @return Whether permission is (probably) granted + */ + boolean testPermission(PermissibleCommandSource source); + + /** + * Gets a short one-line description of this command. + * + *

The help system may display the description in the command list.

+ * + * @param source The source of the help request + * @return A description + */ + Optional getShortDescription(PermissibleCommandSource source); + + /** + * Gets a longer formatted help message about this command. + * + *

It is recommended to use the default text color and style. Sections + * with text actions (e.g. hyperlinks) should be underlined.

+ * + *

Multi-line messages can be created by separating the lines with + * {@code \n}.

+ * + *

The help system may display this message when a source requests + * detailed information about a command.

+ * + * @param source The source of the help request + * @return A help text + */ + Optional getHelp(PermissibleCommandSource source); + + /** + * Gets the usage string of this command. + * + *

A usage string may look like + * {@code [-w <world>] <var1> <var2>}.

+ * + *

The string must not contain the command alias.

+ * + * @param source The source of the help request + * @return A usage string + */ + ChatMessage getUsage(PermissibleCommandSource source); +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandException.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandException.java new file mode 100644 index 000000000..038d4a8dc --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandException.java @@ -0,0 +1,92 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import net.minecraft.text.ChatMessage; + +/** + * Thrown when an executed command raises an error or when execution of + * the command failed. + */ +public class CommandException extends TextMessageException { + private static final long serialVersionUID = 4626722485860074825L; + + private final boolean includeUsage; + + /** + * Constructs a new {@link CommandException} with the given message. + * + * @param message The detail message + */ + public CommandException(ChatMessage message) { + this(message, false); + } + + /** + * Constructs a new {@link CommandException} with the given message and + * the given cause. + * + * @param message The detail message + * @param cause The cause + */ + public CommandException(ChatMessage message, Throwable cause) { + this(message, cause, false); + } + + /** + * Constructs a new {@link CommandException} with the given message. + * + * @param message The detail message + * @param includeUsage Whether to include usage in the exception + */ + public CommandException(ChatMessage message, boolean includeUsage) { + super(message); + this.includeUsage = includeUsage; + } + + /** + * Constructs a new {@link CommandException} with the given message and + * the given cause. + * + * @param message The detail message + * @param cause The cause + * @param includeUsage Whether to include the usage in the exception + */ + public CommandException(ChatMessage message, Throwable cause, boolean includeUsage) { + super(message, cause); + this.includeUsage = includeUsage; + } + + /** + * Gets whether the exception should include usage in + * the presentation of the exception/stack-trace. + * + * @return Whether to include usage in the exception + */ + public boolean shouldIncludeUsage() { + return this.includeUsage; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandManager.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandManager.java new file mode 100644 index 000000000..194e962b5 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandManager.java @@ -0,0 +1,147 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.world.World; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.dispatcher.Dispatcher; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; +import net.legacyfabric.fabric.api.util.Location; + +/** + * A command dispatcher watches for commands (such as those said in chat) + * and dispatches them to the correct command handler. + */ +public interface CommandManager extends Dispatcher { + /** + * Register a given command using the given list of aliases. + * + *

If there is a conflict with one of the aliases (i.e. that alias + * is already assigned to another command), then the alias will be skipped. + * It is possible for there to be no alias to be available out of + * the provided list of aliases, which would mean that the command would not + * be assigned to any aliases.

+ * + *

The first non-conflicted alias becomes the "primary alias."

+ * + * @param callable The command + * @param alias An array of aliases + * @return The registered command mapping, unless no aliases could be + * registered + */ + Optional register(CommandCallable callable, String... alias); + + /** + * Register a given command using the given list of aliases. + * + *

If there is a conflict with one of the aliases (i.e. that alias + * is already assigned to another command), then the alias will be skipped. + * It is possible for there to be no alias to be available out of + * the provided list of aliases, which would mean that the command would + * not be assigned to any aliases.

+ * + *

The first non-conflicted alias becomes the "primary alias."

+ * + * @param callable The command + * @param aliases A list of aliases + * @return The registered command mapping, unless no aliases could be + * registered + * @throws IllegalArgumentException Thrown if {@code plugin} is not a + * plugin instance + */ + Optional register(CommandCallable callable, List aliases); + + /** + * Register a given command using a given list of aliases. + * + *

The provided callback function will be called with a list of aliases + * that are not taken (from the list of aliases that were requested) and + * it should return a list of aliases to actually register. Aliases may be + * removed, and if no aliases remain, then the command will not be + * registered. It may be possible that no aliases are available, and thus + * the callback would receive an empty list. New aliases should not be added + * to the list in the callback as this may cause + * {@link IllegalArgumentException} to be thrown.

+ * + *

The first non-conflicted alias becomes the "primary alias."

+ * + * @param callable The command + * @param aliases A list of aliases + * @param callback The callback + * @return The registered command mapping, unless no aliases could be + * registered + * @throws IllegalArgumentException Thrown if new conflicting aliases are + * added in the callback + */ + Optional register(CommandCallable callable, List aliases, Function, List> callback); + + /** + * Remove a command identified by the given mapping. + * + * @param mapping The mapping + * @return The previous mapping associated with the alias, if one was found + */ + Optional removeMapping(CommandMapping mapping); + + /** + * Gets the number of registered aliases. + * + * @return The number of aliases + */ + int size(); + + /** + * Execute the command based on input arguments. + * + *

The implementing class must perform the necessary permission + * checks.

+ * + * @param source The caller of the command + * @param arguments The raw arguments for this command + * @return The result of a command being processed + */ + @Override + CommandResult process(PermissibleCommandSource source, String arguments); + + /** + * Gets a list of suggestions based on input. + * + *

If a suggestion is chosen by the user, it will replace the last + * word.

+ * + * @param source The command source + * @param arguments The arguments entered up to this point + * @return A list of suggestions + */ + @Override + List getSuggestions(PermissibleCommandSource source, String arguments, @Nullable Location targetPosition); +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandMapping.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandMapping.java new file mode 100644 index 000000000..909714014 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandMapping.java @@ -0,0 +1,64 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import java.util.Set; + +/** + * Provides information about a mapping between a command and its aliases. + * + *

Implementations are not required to implement a sane + * {@link Object#equals(Object)} but may choose to do so.

+ */ +public interface CommandMapping { + /** + * Gets the primary alias. + * + * @return The primary alias + */ + String getPrimaryAlias(); + + /** + * Gets an immutable list of all aliases. + * + *

The returned list must contain at least one entry, of which one must + * be the one returned by {@link #getPrimaryAlias()}.

+ * + *

There may be several versions of the same alias with different + * casing, although generally implementations should ignore the casing + * of aliases.

+ * + * @return A set of aliases + */ + Set getAllAliases(); + + /** + * Gets the callable. + * + * @return The callable + */ + CommandCallable getCallable(); +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandMessageFormatting.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandMessageFormatting.java new file mode 100644 index 000000000..eff66f247 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandMessageFormatting.java @@ -0,0 +1,62 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import net.minecraft.text.ChatMessage; +import net.minecraft.util.Formatting; + +public class CommandMessageFormatting { + private CommandMessageFormatting() { + } + + public static final ChatMessage PIPE_TEXT = ChatMessage.createTextMessage("|"); + public static final ChatMessage SPACE_TEXT = ChatMessage.createTextMessage(" "); + public static final ChatMessage STAR_TEXT = ChatMessage.createTextMessage("*"); + public static final ChatMessage LT_TEXT = ChatMessage.createTextMessage("<"); + public static final ChatMessage GT_TEXT = ChatMessage.createTextMessage(">"); + public static final ChatMessage ELLIPSIS_TEXT = ChatMessage.createTextMessage("…"); + + /** + * Format text to be output as an error directly to a sender. Not necessary + * when creating an exception to be thrown + * + * @param error The error message + * @return The formatted error message. + */ + public static ChatMessage error(ChatMessage error) { + return error.setColor(Formatting.RED); + } + + /** + * Format text to be output as a debug message directly to a sender. + * + * @param debug The debug message + * @return The formatted debug message. + */ + public static ChatMessage debug(ChatMessage debug) { + return debug.setColor(Formatting.GRAY); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandNotFoundException.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandNotFoundException.java new file mode 100644 index 000000000..7498aadf9 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandNotFoundException.java @@ -0,0 +1,69 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import com.google.common.base.Preconditions; + +import net.minecraft.text.ChatMessage; + +/** + * This exception is thrown when a sender tries to execute a command that does + * not exist. + */ +public class CommandNotFoundException extends CommandException { + private static final long serialVersionUID = -7714518367616848051L; + + private final String command; + + /** + * Create an exception with the default message. + * + * @param command The command that was queried for + */ + public CommandNotFoundException(String command) { + this(ChatMessage.createTextMessage("No such command"), command); + } + + /** + * Create an exception with a custom message. + * + * @param message The message + * @param command The command that was queried for + */ + public CommandNotFoundException(ChatMessage message, String command) { + super(message); + this.command = Preconditions.checkNotNull(command, "command"); + } + + /** + * Returns the command that was queried for. + * + * @return The command + */ + public String getCommand() { + return this.command; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandPermissionException.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandPermissionException.java new file mode 100644 index 000000000..7a0948a00 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandPermissionException.java @@ -0,0 +1,62 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import net.minecraft.text.ChatMessage; + +/** + * This exception is thrown when a subject does not have permission to execute + * a command. + */ +public class CommandPermissionException extends CommandException { + private static final long serialVersionUID = -6057386975881181213L; + + /** + * Create an exception with the default message. + */ + public CommandPermissionException() { + this(ChatMessage.createTextMessage("You do not have permission to use this command!")); + } + + /** + * Create a permissions exception with a custom message. + * + * @param message The message + */ + public CommandPermissionException(ChatMessage message) { + super(message); + } + + /** + * Create a permissions exception with a custom message and cause. + * + * @param message the message + * @param cause the cause + */ + public CommandPermissionException(ChatMessage message, Throwable cause) { + super(message, cause); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandResult.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandResult.java new file mode 100644 index 000000000..8c73ab15b --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/CommandResult.java @@ -0,0 +1,279 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +/** + * Represents the result of a command in Sponge. + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class CommandResult { + private static final CommandResult EMPTY = builder().build(); + private static final CommandResult SUCCESS = builder().successCount(1).build(); + private final Optional successCount; + private final Optional affectedBlocks; + private final Optional affectedEntities; + private final Optional affectedItems; + private final Optional queryResult; + + /** + * Returns a {@link Builder}. + * + * @return A new command result builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a new {@link CommandResult} indicating that a command was + * processed. + * + * @return The command result + */ + public static CommandResult empty() { + return EMPTY; + } + + /** + * Returns a result indicating the command was processed with a single + * success. + * + * @return The result + */ + public static CommandResult success() { + return SUCCESS; + } + + /** + * Returns a result indicating the command was processed with a single + * success. + * + * @param count The success count + * @return The result + */ + public static CommandResult successCount(int count) { + return builder().successCount(count).build(); + } + + /** + * Returns a result indicating the command was processed with an + * amount of affected blocks. + * + * @param count The amount of blocks affected + * @return The result + */ + public static CommandResult affectedBlocks(int count) { + return builder().affectedBlocks(count).build(); + } + + /** + * Returns a result indicating the command was processed with an + * amount of affected entities. + * + * @param count The amount of entities affected + * @return The result + */ + public static CommandResult affectedEntities(int count) { + return builder().affectedEntities(count).build(); + } + + /** + * Returns a result indicating the command was processed with an + * amount of affected items. + * + * @param count The amount of items affected + * @return The result + */ + public static CommandResult affectedItems(int count) { + return builder().affectedItems(count).build(); + } + + /** + * Returns a result indicating the command was processed with an + * amount of queries. + * + * @param count The amount of queries + * @return The result + */ + public static CommandResult queryResult(int count) { + return builder().queryResult(count).build(); + } + + /** + * Constructs a new command result. + * + * @param successCount The success count + * @param affectedBlocks The number of affected blocks + * @param affectedEntities The number of affected entities + * @param affectedItems The number of affected items + * @param queryResult The query result + */ + CommandResult(@Nullable Integer successCount, @Nullable Integer affectedBlocks, @Nullable Integer affectedEntities, @Nullable Integer affectedItems, @Nullable Integer queryResult) { + this.successCount = Optional.ofNullable(successCount); + this.affectedBlocks = Optional.ofNullable(affectedBlocks); + this.affectedEntities = Optional.ofNullable(affectedEntities); + this.affectedItems = Optional.ofNullable(affectedItems); + this.queryResult = Optional.ofNullable(queryResult); + } + + /** + * Gets the success count of the command. + * + * @return The success count of the command + */ + public Optional getSuccessCount() { + return this.successCount; + } + + /** + * Gets the number of blocks affected by the command. + * + * @return The number of blocks affected by the command, if such a count + * exists + */ + public Optional getAffectedBlocks() { + return this.affectedBlocks; + } + + /** + * Gets the number of entities affected by the command. + * + * @return The number of entities affected by the command, if such a count + * exists + */ + public Optional getAffectedEntities() { + return this.affectedEntities; + } + + /** + * Gets the number of items affected by the command. + * + * @return The number of items affected by the command, if such a count + * exists + */ + public Optional getAffectedItems() { + return this.affectedItems; + } + + /** + * Gets the query result of the command, e.g. the time of the day, + * an amount of money or a player's amount of XP. + * + * @return The query result of the command, if one exists + */ + public Optional getQueryResult() { + return this.queryResult; + } + + /** + * A builder for {@link CommandResult}s. + */ + public static class Builder { + @Nullable + private Integer successCount; + @Nullable + private Integer affectedBlocks; + @Nullable + private Integer affectedEntities; + @Nullable + private Integer affectedItems; + @Nullable + private Integer queryResult; + + Builder() { + } + + /** + * Sets if the command has been processed. + * + * @param successCount If the command has been processed + * @return This builder, for chaining + */ + public Builder successCount(@Nullable Integer successCount) { + this.successCount = successCount; + return this; + } + + /** + * Sets the amount of blocks affected by the command. + * + * @param affectedBlocks The amount of blocks affected by the command + * @return This builder, for chaining + */ + public Builder affectedBlocks(@Nullable Integer affectedBlocks) { + this.affectedBlocks = affectedBlocks; + return this; + } + + /** + * Sets the amount of entities affected by the command. + * + * @param affectedEntities The amount of entities affected by the + * command + * @return This builder, for chaining + */ + public Builder affectedEntities(@Nullable Integer affectedEntities) { + this.affectedEntities = affectedEntities; + return this; + } + + /** + * Sets the amount of items affected by the command. + * + * @param affectedItems The amount of items affected by the command + * @return This builder, for chaining + */ + public Builder affectedItems(@Nullable Integer affectedItems) { + this.affectedItems = affectedItems; + return this; + } + + /** + * Sets the query result of the command, e.g. the time of the day, + * an amount of money or a player's amount of XP. + * + * @param queryResult The query result of the command + * @return This builder, for chaining + */ + public Builder queryResult(@Nullable Integer queryResult) { + this.queryResult = queryResult; + return this; + } + + /** + * Builds the {@link CommandResult}. + * + * @return A CommandResult with the specified settings + */ + public CommandResult build() { + return new CommandResult(this.successCount, this.affectedBlocks, this.affectedEntities, this.affectedItems, this.queryResult); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/ImmutableCommandMapping.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/ImmutableCommandMapping.java new file mode 100644 index 000000000..93d961ef1 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/ImmutableCommandMapping.java @@ -0,0 +1,117 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; + +/** + * An immutable command mapping instance that returns the same objects that + * this instance is constructed with. + */ +public final class ImmutableCommandMapping implements CommandMapping { + private final String primary; + private final Set aliases; + private final CommandCallable callable; + + /** + * Create a new instance. + * + * @param callable The command callable + * @param primary The primary alias + * @param alias A list of all aliases + * @throws IllegalArgumentException Thrown if aliases are duplicated + */ + public ImmutableCommandMapping(CommandCallable callable, String primary, String... alias) { + this(callable, primary, Arrays.asList(Preconditions.checkNotNull(alias, "alias"))); + } + + /** + * Create a new instance. + * + * @param callable The command callable + * @param primary The primary alias + * @param aliases A collection of all aliases + * @throws IllegalArgumentException Thrown if aliases are duplicated + */ + public ImmutableCommandMapping(CommandCallable callable, String primary, Collection aliases) { + Preconditions.checkNotNull(primary, "primary"); + Preconditions.checkNotNull(aliases, "aliases"); + this.primary = primary; + this.aliases = new HashSet<>(aliases); + this.aliases.add(primary); + this.callable = Preconditions.checkNotNull(callable, "callable"); + } + + @Override + public String getPrimaryAlias() { + return this.primary; + } + + @Override + public Set getAllAliases() { + return ImmutableSet.copyOf(this.aliases); + } + + @Override + public CommandCallable getCallable() { + return this.callable; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || this.getClass() != o.getClass()) { + return false; + } + + ImmutableCommandMapping that = (ImmutableCommandMapping) o; + return this.primary.equals(that.primary) && this.aliases.equals(that.aliases) && this.callable.equals(that.callable); + } + + @Override + public int hashCode() { + return Objects.hash(this.primary, this.aliases, this.callable); + } + + @Override + public String toString() { + return "ImmutableCommandMapping{" + + "primary='" + this.primary + '\'' + + ", aliases=" + this.aliases + + ", spec=" + this.callable + + '}'; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/InvocationCommandException.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/InvocationCommandException.java new file mode 100644 index 000000000..d60b14bee --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/InvocationCommandException.java @@ -0,0 +1,46 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import net.minecraft.text.ChatMessage; + +/** + * Thrown when invocation of a command fails, wrapping the exception that + * is thrown. + */ +public class InvocationCommandException extends CommandException { + private static final long serialVersionUID = 2123904283741023948L; + + /** + * Constructs a new exception with the given message and the given cause. + * + * @param message The detail message + * @param cause The cause + */ + public InvocationCommandException(ChatMessage message, Throwable cause) { + super(message, cause); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/TextMessageException.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/TextMessageException.java new file mode 100644 index 000000000..bf0851b62 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/TextMessageException.java @@ -0,0 +1,98 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge; + +import net.minecraft.text.ChatMessage; + +/** + * A subclass of Exception that contains a rich message that is an instance of + * {@link Text} rather than a String. This allows formatted and localized + * exception messages. + */ +public class TextMessageException extends Exception { + private static final long serialVersionUID = -5281221645176698853L; + + private final ChatMessage message; + + /** + * Constructs a new {@link TextMessageException}. + */ + public TextMessageException() { + this.message = null; + } + + /** + * Constructs a new {@link TextMessageException} with the given message. + * + * @param message The detail message + */ + public TextMessageException(ChatMessage message) { + this.message = message; + } + + /** + * Constructs a new {@link TextMessageException} with the given message and + * cause. + * + * @param message The detail message + * @param throwable The cause + */ + public TextMessageException(ChatMessage message, Throwable throwable) { + super(throwable); + this.message = message; + } + + /** + * Constructs a new {@link TextMessageException} with the given cause. + * + * @param throwable The cause + */ + public TextMessageException(Throwable throwable) { + super(throwable); + this.message = null; + } + + @Override + public String getMessage() { + ChatMessage message = this.getText(); + return message == null ? null : message.toString(true); + } + + /** + * Returns the text message for this exception, or null if nothing is + * present. + * + * @return The text for this message + */ + public ChatMessage getText() { + return this.message; + } + + @Override + public String getLocalizedMessage() { + return this.getMessage(); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/ArgumentParseException.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/ArgumentParseException.java new file mode 100644 index 000000000..f117f17b8 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/ArgumentParseException.java @@ -0,0 +1,158 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import com.google.common.base.Strings; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandException; + +/** + * Exception thrown when an error occurs while parsing arguments. + */ +public class ArgumentParseException extends CommandException { + private static final long serialVersionUID = -8555316116315990226L; + + private final String source; + private final int position; + + /** + * Return a new {@link ArgumentParseException} with the given message, source and position. + * + * @param message The message to use for this exception + * @param source The source string being parsed + * @param position The current position in the source string + */ + public ArgumentParseException(ChatMessage message, String source, int position) { + super(message, true); + this.source = source; + this.position = position; + } + + /** + * Return a new {@link ArgumentParseException} with the given message, cause, source and position. + * + * @param message The message to use for this exception + * @param cause The cause for this exception + * @param source The source string being parsed + * @param position The current position in the source string + */ + public ArgumentParseException(ChatMessage message, Throwable cause, String source, int position) { + super(message, cause, true); + this.source = source; + this.position = position; + } + + @Override + public ChatMessage getText() { + ChatMessage superText = super.getText(); + + if (this.source == null || this.source.isEmpty()) { + return super.getText(); + } else if (superText == null) { + return ChatMessage.createTextMessage(this.getAnnotatedPosition()); + } else { + return ChatMessage.createTextMessage(superText + "\n" + this.getAnnotatedPosition()); + } + } + + private ChatMessage getSuperText() { + return super.getText(); + } + + /** + * Return a string pointing to the position of the arguments when this + * exception occurs. + * + * @return The appropriate position string + */ + public String getAnnotatedPosition() { + String source = this.source; + int position = this.position; + + if (source.length() > 80) { + if (position >= 37) { + int startPos = position - 37; + int endPos = Math.min(source.length(), position + 37); + + if (endPos < source.length()) { + source = "..." + source.substring(startPos, endPos) + "..."; + } else { + source = "..." + source.substring(startPos, endPos); + } + + position -= 40; + } else { + source = source.substring(0, 77) + "..."; + } + } + + return source + "\n" + Strings.repeat(" ", position) + "^"; + } + + /** + * Gets the position of the last fetched argument in the provided source + * string. + * + * @return The source string to get position for + */ + public int getPosition() { + return this.position; + } + + /** + * Returns the source string arguments are being parsed from. + * + * @return The source string + */ + public String getSourceString() { + return this.source; + } + + /** + * An {@link ArgumentParseException} where the usage is already specified. + */ + public static class WithUsage extends ArgumentParseException { + private static final long serialVersionUID = -786214501012293475L; + + private final ChatMessage usage; + + public WithUsage(ArgumentParseException wrapped, ChatMessage usage) { + super(wrapped.getSuperText(), wrapped.getCause(), wrapped.getSourceString(), wrapped.getPosition()); + this.usage = usage; + } + + /** + * Gets the usage associated with this exception. + * + * @return The usage + */ + public ChatMessage getUsage() { + return this.usage; + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/ChildCommandElementExecutor.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/ChildCommandElementExecutor.java new file mode 100644 index 000000000..e74f22154 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/ChildCommandElementExecutor.java @@ -0,0 +1,284 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimaps; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; +import net.minecraft.world.World; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandCallable; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMapping; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMessageFormatting; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandResult; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.dispatcher.SimpleDispatcher; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.spec.CommandExecutor; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.spec.CommandSpec; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; +import net.legacyfabric.fabric.api.util.Location; + +public class ChildCommandElementExecutor extends CommandElement implements CommandExecutor { + private static final AtomicInteger COUNTER = new AtomicInteger(); + private static final CommandElement NONE = GenericArguments.none(); + + @Nullable + private final CommandExecutor fallbackExecutor; + @Nullable + private final CommandElement fallbackElements; + private final SimpleDispatcher dispatcher = new SimpleDispatcher(SimpleDispatcher.FIRST_DISAMBIGUATOR); + private final boolean fallbackOnFail; + + /** + * Create a new combined argument element and executor to handle the + * parsing and execution of child commands. + * + * @param fallbackExecutor The executor to execute if the child command + * has been marked optional (Generally when this is wrapped in a + * {@link GenericArguments#optional(CommandElement)} + * @param fallbackElements The alternate {@link CommandElement}s that should + * be parsed if a child element fails to be parsed + * @param fallbackOnFail If true, then if a child command cannot parse the + * elements, the exception is discarded and the parent command attempts + * to parse the elements. If false, a child command will not pass + * control back to the parent, displaying its own exception message + */ + public ChildCommandElementExecutor(@Nullable CommandExecutor fallbackExecutor, @Nullable CommandElement fallbackElements, boolean fallbackOnFail) { + super(ChatMessage.createTextMessage("child" + COUNTER.getAndIncrement())); + this.fallbackExecutor = fallbackExecutor; + this.fallbackElements = NONE == fallbackElements ? null : fallbackElements; + this.fallbackOnFail = fallbackOnFail; + } + + /** + * Register a child command for a given set of aliases. + * + * @param callable The command to register + * @param aliases The aliases to register it as + * @return The child command's mapping, if present + */ + public Optional register(CommandCallable callable, List aliases) { + return this.dispatcher.register(callable, aliases); + } + + /** + * Register a child command for a given set of aliases. + * + * @param callable The command to register + * @param aliases The aliases to register it as + * @return The child command's mapping, if present + */ + public Optional register(CommandCallable callable, String... aliases) { + return this.dispatcher.register(callable, aliases); + } + + @Override + public List complete(final PermissibleCommandSource src, CommandArgs args, CommandContext context) { + List completions = Lists.newArrayList(); + + if (this.fallbackElements != null) { + CommandArgs.Snapshot state = args.getSnapshot(); + completions.addAll(this.fallbackElements.complete(src, args, context)); + args.applySnapshot(state); + } + + final Optional commandComponent = args.nextIfPresent(); + + if (!commandComponent.isPresent()) { + return ImmutableList.copyOf(this.filterCommands(src)); + } + + if (args.hasNext()) { + Optional child = this.dispatcher.get(commandComponent.get(), src); + + if (!child.isPresent()) { + return ImmutableList.of(); + } + + if (child.get().getCallable() instanceof CommandSpec) { + return ((CommandSpec) child.get().getCallable()).complete(src, args, context); + } + + args.nextIfPresent(); + final String arguments = args.getRaw().substring(args.getRawPosition()); + + while (args.hasNext()) { + args.nextIfPresent(); + } + + try { + return child.get() + .getCallable() + .getSuggestions(src, arguments, context.>getOne(CommandContext.TARGET_BLOCK_ARG).orElse(null)); + } catch (CommandException e) { + ChatMessage eText = e.getText(); + + if (eText != null) { + src.method_5505(CommandMessageFormatting.error(eText)); + } + + return ImmutableList.of(); + } + } + + completions.addAll(this.filterCommands(src).stream() + .filter(((input) -> { + String test = commandComponent.get(); + return input.toLowerCase().startsWith(test.toLowerCase()); + })) + .collect(Collectors.toList())); + return completions; + } + + private Set filterCommands(final PermissibleCommandSource src) { + return Multimaps.filterValues( + this.dispatcher.getAll(), + (input) -> + input != null && input.getCallable().testPermission(src) + ).keys().elementSet(); + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + if (this.fallbackExecutor != null && !args.hasNext()) { + if (this.fallbackElements != null) { + // there might be optionals to take account of that would parse this successfully. + this.fallbackElements.parse(source, args, context); + } + + return; // execute the fallback regardless in this scenario. + } + + CommandArgs.Snapshot state = args.getSnapshot(); + final String key = args.next(); + Optional optionalCommandMapping = this.dispatcher.get(key, source).map(Function.identity()); + + if (optionalCommandMapping.isPresent()) { + final CommandMapping mapping = optionalCommandMapping.get(); + + try { + if ((mapping.getCallable() instanceof CommandSpec)) { + CommandSpec spec = ((CommandSpec) mapping.getCallable()); + spec.populateContext(source, args, context); + } else { + if (args.hasNext()) { + args.next(); + context.putArg(this.getUntranslatedKey() + "_args", args.getRaw().substring(args.getRawPosition())); + } + + while (args.hasNext()) { + args.next(); + } + } + + // Success, add to context now so that we don't execute the wrong executor in the first place. + context.putArg(this.getUntranslatedKey(), mapping); + } catch (ArgumentParseException ex) { + // If we get here, fallback to the elements, if they exist. + args.applySnapshot(state); + + if (this.fallbackOnFail && this.fallbackElements != null) { + this.fallbackElements.parse(source, args, context); + return; + } + + // Get the usage + args.next(); + + if (ex instanceof ArgumentParseException.WithUsage) { + // This indicates a previous child failed, so we just prepend our child + throw new ArgumentParseException.WithUsage(ex, ChatMessage.createTextMessage(key + " " + ((ArgumentParseException.WithUsage) ex).getUsage().toString())); + } + + throw new ArgumentParseException.WithUsage(ex, ChatMessage.createTextMessage(key + " " + mapping.getCallable().getUsage(source).toString())); + } + } else { + // Not a child, so let's continue with the fallback. + if (this.fallbackExecutor != null && this.fallbackElements != null) { + args.applySnapshot(state); + this.fallbackElements.parse(source, args, context); + } else { + // If we have no elements to parse, then we throw this error - this is the only element + // so specifying it implicitly means we have a child command to execute. + throw args.createError(ChatMessage.createTextMessage(String.format("Input command %s was not a valid subcommand!", key))); + } + } + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return null; + } + + @Override + public CommandResult execute(PermissibleCommandSource src, CommandContext args) throws CommandException { + CommandMapping mapping = args.getOne(this.getUntranslatedKey()).orElse(null); + + if (mapping == null) { + if (this.fallbackExecutor == null) { + throw new CommandException(ChatMessage.createTextMessage(String.format("Invalid subcommand state -- no more than one mapping may be provided for child arg %s", this.getKey()))); + } + + return this.fallbackExecutor.execute(src, args); + } + + if (mapping.getCallable() instanceof CommandSpec) { + CommandSpec spec = ((CommandSpec) mapping.getCallable()); + spec.checkPermission(src); + return spec.getExecutor().execute(src, args); + } + + final String arguments = args.getOne(this.getUntranslatedKey() + "_args").orElse(""); + return mapping.getCallable().process(src, arguments); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + ChatMessage usage = this.dispatcher.getUsage(src); + + if (this.fallbackElements == null) { + return usage; + } + + ChatMessage elementUsage = this.fallbackElements.getUsage(src); + + if (elementUsage.toString(true).isEmpty()) { + return usage; + } + + return ChatMessage.createTextMessage("").addUsing(usage).addUsing(CommandMessageFormatting.PIPE_TEXT).addUsing(elementUsage); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandArgs.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandArgs.java new file mode 100644 index 000000000..40c5ebeea --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandArgs.java @@ -0,0 +1,347 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.parsing.SingleArg; + +/** + * Holder for command arguments. + */ +public final class CommandArgs { + private final String rawInput; + private final List args; + private int index = -1; + + /** + * Create a new CommandArgs instance with the given raw input and arguments. + * + * @param rawInput Raw input + * @param args Arguments extracted from the raw input + */ + public CommandArgs(String rawInput, List args) { + this.rawInput = rawInput; + this.args = new ArrayList<>(args); + } + + /** + * Return whether more arguments remain to be read. + * + * @return Whether more arguments remain + */ + public boolean hasNext() { + return this.index + 1 < this.args.size(); + } + + /** + * Returns true if: + * + *
    + *
  • The next element returned by {@link #next()} is the last
  • + *
  • {@link #hasNext()} is false
  • + *
+ * Else returns false. + * + * @return True if a completion can occur + */ + public boolean canComplete() { + return this.index + 2 >= this.args.size(); + } + + /** + * Try to read the next argument without advancing the current index. + * + * @return The next argument + * @throws ArgumentParseException if not enough arguments are present + */ + public String peek() throws ArgumentParseException { + if (!this.hasNext()) { + throw this.createError(ChatMessage.createTextMessage("Not enough arguments!")); + } + + return this.args.get(this.index + 1).getValue(); + } + + /** + * Try to read the next argument, advancing the current index if successful. + * + * @return The next argument + * @throws ArgumentParseException if not enough arguments are present + */ + public String next() throws ArgumentParseException { + if (!this.hasNext()) { + throw this.createError(ChatMessage.createTextMessage("Not enough arguments!")); + } + + return this.args.get(++this.index).getValue(); + } + + /** + * Try to read the next argument, advancing the current index if successful + * or returning an absent optional if not. + * + * @return The optional next argument. + */ + public Optional nextIfPresent() { + return this.hasNext() ? Optional.of(this.args.get(++this.index).getValue()) : Optional.empty(); + } + + /** + * Create a parse exception with the provided message which has the position + * of the last parsed argument attached. The returned exception must be + * thrown at the target + * + * @param message The message for the exception + * @return the newly created, but unthrown exception + */ + public ArgumentParseException createError(ChatMessage message) { + return new ArgumentParseException(message, this.rawInput, this.index < 0 ? 0 : this.args.get(this.index).getStartIdx()); + } + + /** + * Gets a list of all arguments as a string. The returned list is immutable. + * + * @return all arguments + */ + public List getAll() { + return Collections.unmodifiableList(this.args.stream().map(SingleArg::getValue).collect(Collectors.toList())); + } + + List getArgs() { + return this.args; + } + + /** + * Return this arguments object's current state. Can be used to reset with + * the {@link #setState(Object)} method. + * + * @return The current state + * @deprecated Use {@link #getSnapshot()} and + * {@link #applySnapshot(Snapshot)} instead + */ + @Deprecated + public Object getState() { + return this.getSnapshot(); + } + + /** + * Restore the arguments object's state to a state previously used. + * + * @param state the previous state + * @deprecated Use {@link #getSnapshot()} and + * {@link #applySnapshot(Snapshot)} instead + */ + @Deprecated + public void setState(Object state) { + if (!(state instanceof Snapshot)) { + throw new IllegalArgumentException("Provided state was not of appropriate format returned by getState!"); + } + + this.applySnapshot((Snapshot) state, false); // keep parity with before + } + + /** + * Return the raw string used to provide input to this arguments object. + * + * @return The raw input + */ + public String getRaw() { + return this.rawInput; + } + + /** + * Get an arg at the specified position. + * + * @param index index of the element to return + */ + public String get(int index) { + return this.args.get(index).getValue(); + } + + /** + * Insert an arg as the next arg to be returned by {@link #next()}. + * + * @param value The argument to insert + */ + public void insertArg(String value) { + int index = this.index < 0 ? 0 : this.args.get(this.index).getEndIdx(); + this.args.add(this.index + 1, new SingleArg(value, index, index)); + } + + /** + * Remove the arguments parsed between startState and endState. + * + * @param startState The starting state + * @param endState The ending state + * @deprecated Use with {@link #getSnapshot()} instead of + * {@link #getState()} with {@link #removeArgs(Snapshot, Snapshot)} + */ + @Deprecated + public void removeArgs(Object startState, Object endState) { + if (!(startState instanceof Integer) || !(endState instanceof Integer)) { + throw new IllegalArgumentException("One of the states provided was not of the correct type!"); + } + + this.removeArgs((int) startState, (int) endState); + } + + /** + * Remove the arguments parsed between two snapshots. + * + * @param startSnapshot The starting state + * @param endSnapshot The ending state + */ + public void removeArgs(Snapshot startSnapshot, Snapshot endSnapshot) { + this.removeArgs(startSnapshot.index, endSnapshot.index); + } + + private void removeArgs(int startIdx, int endIdx) { + if (this.index >= startIdx) { + if (this.index < endIdx) { + this.index = startIdx - 1; + } else { + this.index -= (endIdx - startIdx) + 1; + } + } + + for (int i = startIdx; i <= endIdx; ++i) { + this.args.remove(startIdx); + } + } + + /** + * Returns the number of arguments. + * + * @return the number of arguments + */ + public int size() { + return this.args.size(); + } + + /** + * Go back to the previous argument. + */ + void previous() { + if (this.index > -1) { + --this.index; + } + } + + /** + * Gets the current position in raw input. + * + * @return the raw position + */ + public int getRawPosition() { + return this.index < 0 ? 0 : this.args.get(this.index).getStartIdx(); + } + + /** + * Gets a snapshot of the data inside this context to allow it to be + * restored later. + * + * @return The {@link Snapshot} containing the current state of the + * {@link CommandArgs} + */ + public Snapshot getSnapshot() { + return new Snapshot(this.index, this.args); + } + + /** + * Resets a {@link CommandArgs} to a previous state using a previously + * created {@link Snapshot}. + * + * @param snapshot The {@link Snapshot} to restore this context + * with + */ + public void applySnapshot(Snapshot snapshot) { + this.applySnapshot(snapshot, true); + } + + /** + * Resets a {@link CommandArgs} to a previous state using a previously + * created {@link Snapshot}. + * + *

If resetArgs is set to false, this snapshot will not reset the + * argument list to its previous state, only the index.

+ * + * @param snapshot The {@link Snapshot} to restore this context + * with + * @param resetArgs Whether to restore the argument list + */ + public void applySnapshot(Snapshot snapshot, boolean resetArgs) { + this.index = snapshot.index; + + if (resetArgs) { + this.args.clear(); + this.args.addAll(snapshot.args); + } + } + + /** + * A snapshot of a {@link CommandArgs}. This object does not contain any + * public API methods, a snapshot should be considered a black box. + */ + public final class Snapshot { + final int index; + final ImmutableList args; + + Snapshot(int index, List args) { + this.index = index; + this.args = ImmutableList.copyOf(args); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || this.getClass() != o.getClass()) { + return false; + } + + Snapshot snapshot = (Snapshot) o; + return this.index == snapshot.index + && Objects.equals(this.args, snapshot.args); + } + + @Override + public int hashCode() { + return Objects.hash(this.index, this.args); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandContext.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandContext.java new file mode 100644 index 000000000..64cb2b7fd --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandContext.java @@ -0,0 +1,334 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import java.util.Collection; +import java.util.Collections; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Set; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandException; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +/** + * Context that a command is executed in. + * This object stores parsed arguments from other commands + */ +public final class CommandContext { + /** + * The argument key for a target block position that may be present + * during tab completion, of type {@link net.legacyfabric.fabric.api.util.Location Location<World>}. + */ + public static final String TARGET_BLOCK_ARG = "targetblock-pos048658"; // Random junk afterwards so we don't accidentally conflict with other args + + /** + * The argument key to indicate that a tab completion is taking place. + */ + public static final String TAB_COMPLETION = "tab-complete-50456"; // Random junk afterwards so we don't accidentally conflict with other args + + private final Multimap parsedArgs; + private final Set definedFlags; + + /** + * Create a new empty CommandContext. + */ + public CommandContext() { + this.parsedArgs = ArrayListMultimap.create(); + this.definedFlags = Sets.newHashSet(); + } + + /** + * Gets all values for the given argument. May return an empty list if no + * values are present. + * + * @param key The key to get values for + * @param the type of value to get + * @return the collection of all values + */ + @SuppressWarnings("unchecked") + public Collection getAll(String key) { + return Collections.unmodifiableCollection((Collection) this.parsedArgs.get(key)); + } + + /** + * Gets all values for the given argument. May return an empty list if no + * values are present. + * + * @param key The key to get values for + * @param the type of value to get + * @return the collection of all values + */ + public Collection getAll(ChatMessage key) { + return this.getAll(CommandContext.textToArgKey(key)); + } + + /** + * Gets the value for the given key if the key has only one value. + * + *

An empty {@link Optional} indicates that there are either zero or more + * than one values for the given key. Use {@link #hasAny(Text)} to verify + * which.

+ * + * @param key the key to get + * @param the expected type of the argument + * @return the argument + */ + @SuppressWarnings("unchecked") + public Optional getOne(String key) { + Collection values = this.parsedArgs.get(key); + + if (values.size() != 1) { + return Optional.empty(); + } + + return Optional.ofNullable((T) values.iterator().next()); + } + + /** + * Gets the value for the given key if the key has only one value. + * + *

An empty {@link Optional} indicates that there are either zero or more + * than one values for the given key. Use {@link #hasAny(Text)} to verify + * which.

+ * + * @param key the key to get + * @param the expected type of the argument + * @return the argument + */ + public Optional getOne(ChatMessage key) { + return this.getOne(CommandContext.textToArgKey(key)); + } + + /** + * Gets the value for the given key if the key has only one value, throws an + * exception otherwise. + * + * @param key the key to get + * @param the expected type of the argument + * @return the argument + * @throws NoSuchElementException if there is no element with the + * associated key + * @throws IllegalArgumentException if there are more than one element + * associated with the key (thus, the argument is illegal in this + * context) + * @throws ClassCastException if the element type is not what is expected + * by the caller + */ + @SuppressWarnings("unchecked") + public T requireOne(String key) throws NoSuchElementException, IllegalArgumentException, ClassCastException { + Collection values = this.parsedArgs.get(key); + + if (values.size() == 1) { + return (T) values.iterator().next(); + } else if (values.isEmpty()) { + throw new NoSuchElementException(); + } + + throw new IllegalArgumentException(); + } + + /** + * Gets the value for the given key if the key has only one value, throws an + * exception otherwise. + * + * @param key the key to get + * @param the expected type of the argument + * @return the argument + * @throws NoSuchElementException if there is no element with the + * associated key + * @throws IllegalArgumentException if there are more than one element + * associated with the key (thus, the argument is illegal in this + * context) + * @throws ClassCastException if the element type is not what is expected + */ + public T requireOne(ChatMessage key) + throws NoSuchElementException, IllegalArgumentException, ClassCastException { + return this.requireOne(CommandContext.textToArgKey(key)); + } + + /** + * Insert an argument into this context. + * + * @param key the key to store the arg under + * @param value the value for this argument + */ + public void putArg(String key, Object value) { + Preconditions.checkNotNull(value, "value"); + this.parsedArgs.put(key, value); + } + + /** + * Insert an argument into this context. + * + * @param key the key to store the arg under + * @param value the value for this argument + */ + public void putArg(ChatMessage key, Object value) { + this.putArg(CommandContext.textToArgKey(key), value); + } + + /** + * Defines the flag as being present when parsing this context. + * + * @param key the key for the flag defined + */ + public void addFlag(String key) { + this.definedFlags.add(key); + } + + /** + * Defines the flag as being present when parsing this context. + * + * @param key the key for the flag defined + */ + public void addFlag(ChatMessage key) { + this.addFlag(CommandContext.textToArgKey(key)); + } + + /** + * Perform a permissions check, throwing an exception if the required + * permissions are not present. + * + * @param commander the source to check against + * @param permission The permission to check + * @throws CommandException if the source does not have permission + */ + public void checkPermission(PermissibleCommandSource commander, String permission) throws CommandException { + if (!commander.hasPermission(permission)) { + throw new CommandException(ChatMessage.createTextMessage("You do not have permission to use this command!")); + } + } + + /** + * Returns whether this context has any value for the given argument key. + * + * @param key The key to look up + * @return whether there are any values present + */ + public boolean hasAny(String key) { + return this.parsedArgs.containsKey(key); + } + + /** + * Returns whether this context has any value for the given argument key. + * + * @param key The key to look up + * @return whether there are any values present + */ + public boolean hasAny(ChatMessage key) { + return this.hasAny(CommandContext.textToArgKey(key)); + } + + /** + * Returns whether the given flag has been defined in this context. + * + * @param key The key to look up + * @return whether the flag is defined + */ + public boolean hasFlag(String key) { + return this.definedFlags.contains(key); + } + + /** + * Returns whether the given flag has been defined in this context. + * + * @param key The key to look up + * @return whether the flag is defined + */ + public boolean hasFlag(ChatMessage key) { + return this.hasFlag(CommandContext.textToArgKey(key)); + } + + /** + * Gets a snapshot of the data inside this context to allow it to be + * restored later. + * + *

This is only guaranteed to create a shallow copy of the + * backing store. If any value is mutable, any changes to that value + * will be reflected in this snapshot. It is therefore not recommended + * that you keep this snapshot around for longer than is necessary.

+ * + * @return The {@link Snapshot} containing the current state of the + * {@link CommandContext} + */ + public Snapshot createSnapshot() { + return new Snapshot(this.parsedArgs, this.definedFlags); + } + + /** + * Resets a {@link CommandContext} to a previous state using a previously + * created {@link Snapshot}. + * + * @param snapshot The {@link Snapshot} to restore this context with + */ + public void applySnapshot(Snapshot snapshot) { + this.parsedArgs.clear(); + this.parsedArgs.putAll(snapshot.args); + this.definedFlags.clear(); + this.definedFlags.addAll(snapshot.flags); + } + + /** + * A snapshot of a {@link CommandContext}. This object does not contain any + * public API methods, a snapshot should be considered a black box. + */ + public static final class Snapshot { + final Multimap args; + final Set flags; + + Snapshot(Multimap args, Set flags) { + this.args = ArrayListMultimap.create(args); + this.flags = Sets.newHashSet(flags); + } + } + + /** + * Converts a {@link Text} into a String key. + * + * @param key the text to be converted into a string key + * @return the string key. if {@code key} is a {@link TranslatableText}, the translation key. + */ + public static String textToArgKey(@Nullable ChatMessage key) { + if (key == null) { + return null; + } + + if (key.getTranslate() != null) { // Use translation key + return key.getTranslate(); + } + + return key.toString(true); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandElement.java new file mode 100644 index 000000000..9222f1d1d --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandElement.java @@ -0,0 +1,155 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.text.ChatMessage; + +import net.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; + +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +/** + * Represents a command argument element. + */ +public abstract class CommandElement { + @Nullable + private final ChatMessage key; + + protected CommandElement(@Nullable ChatMessage key) { + this.key = key; + } + + /** + * Return the key to be used for this object. + * + * @return the user-facing representation of the key + */ + @Nullable + public ChatMessage getKey() { + return this.key; + } + + /** + * Return the plain key, to be used when looking up this command element in + * a {@link CommandContext}. If the key is a {@link TranslatableText}, this + * is the translation's id. Otherwise, this is the result of + * {@link Text#computeValue()}. + * + * @return the raw key + */ + @Nullable + public String getUntranslatedKey() { + return CommandContext.textToArgKey(this.key); + } + + /** + * Attempt to extract a value for this element from the given arguments and + * put it in the given context. This method normally delegates to + * {@link #parseValue(PermissibleCommandSource, CommandArgs)} for getting the values. + * This method is expected to have no side-effects for the source, meaning + * that executing it will not change the state of the {@link PermissibleCommandSource} + * in any way. + * + * @param source The source to parse for + * @param args The args to extract from + * @param context The context to supply to + * @throws ArgumentParseException if unable to extract a value + */ + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + Object val = this.parseValue(source, args); + String key = this.getUntranslatedKey(); + + if (key != null && val != null) { + if (val instanceof Iterable) { + for (Object ent : ((Iterable) val)) { + context.putArg(key, ent); + } + } else { + context.putArg(key, val); + } + } + } + + /** + * Attempt to extract a value for this element from the given arguments. + * This method is expected to have no side-effects for the source, meaning + * that executing it will not change the state of the {@link PermissibleCommandSource} + * in any way. + * + * @param source The source to parse for + * @param args the arguments + * @return The extracted value + * @throws ArgumentParseException if unable to extract a value + */ + @Nullable + public abstract Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException; + + /** + * Fetch completions for command arguments. + * + * @param src The source requesting tab completions + * @param args The arguments currently provided + * @param context The context to store state in + * @return Any relevant completions + */ + public abstract List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context); + + /** + * Return a usage message for this specific argument. + * + * @param src The source requesting usage + * @return The formatted usage + */ + public ChatMessage getUsage(PermissibleCommandSource src) { + return this.getKey() == null ? ChatMessage.createTextMessage("") : ChatMessage.createTextMessage("<" + this.getKey().toString() + ">"); + } + + public MinecraftServer getServer() { + MinecraftServer minecraftServer = null; + + try { + minecraftServer = MinecraftServer.getServer(); + } catch (RuntimeException e1) { + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) { + minecraftServer = (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } else { + try { + minecraftServer = net.minecraft.client.MinecraftClient.getInstance().getServer(); + } catch (RuntimeException e2) { + e2.printStackTrace(); + } + } + } + + return minecraftServer; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandFlags.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandFlags.java new file mode 100644 index 000000000..d016ccdc1 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/CommandFlags.java @@ -0,0 +1,551 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public final class CommandFlags extends CommandElement { + @Nullable + private final CommandElement childElement; + private final Map, CommandElement> usageFlags; + private final Map shortFlags; + private final Map longFlags; + private final UnknownFlagBehavior unknownShortFlagBehavior; + private final UnknownFlagBehavior unknownLongFlagBehavior; + private final boolean anchorFlags; + + protected CommandFlags(@Nullable CommandElement childElement, Map, CommandElement> usageFlags, Map shortFlags, Map longFlags, UnknownFlagBehavior unknownShortFlagBehavior, UnknownFlagBehavior unknownLongFlagBehavior, boolean anchorFlags) { + super(null); + this.childElement = childElement; + this.usageFlags = usageFlags; + this.shortFlags = shortFlags; + this.longFlags = longFlags; + this.unknownShortFlagBehavior = unknownShortFlagBehavior; + this.unknownLongFlagBehavior = unknownLongFlagBehavior; + this.anchorFlags = anchorFlags; + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + CommandArgs.Snapshot state = args.getSnapshot(); + + while (args.hasNext()) { + String arg = args.next(); + + if (arg.startsWith("-")) { + CommandArgs.Snapshot start = args.getSnapshot(); + boolean remove; + + if (arg.startsWith("--")) { // Long flag + remove = this.parseLongFlag(source, arg.substring(2), args, context); + } else { + remove = this.parseShortFlags(source, arg.substring(1), args, context); + } + + if (remove) { + args.removeArgs(start, args.getSnapshot()); + } + } else if (this.anchorFlags) { + break; + } + } + + // We removed the arguments so we don't parse them as they have already been parsed as flags, + // so don't restore them here! + args.applySnapshot(state, false); + + if (this.childElement != null) { + this.childElement.parse(source, args, context); + } + } + + private boolean parseLongFlag(PermissibleCommandSource source, String longFlag, CommandArgs args, CommandContext context) throws ArgumentParseException { + String[] flagSplit = longFlag.split("=", 2); + String flag = flagSplit[0].toLowerCase(); + CommandElement element = this.longFlags.get(flag); + + if (element == null) { + switch (this.unknownLongFlagBehavior) { + case ERROR: + throw args.createError(ChatMessage.createTextMessage(String.format("Unknown long flag %s specified", flagSplit[0]))); + case ACCEPT_NONVALUE: + context.addFlag(flag); + context.putArg(flag, flagSplit.length == 2 ? flagSplit[1] : true); + return true; + case ACCEPT_VALUE: + context.addFlag(flag); + context.putArg(flag, flagSplit.length == 2 ? flagSplit[1] : args.next()); + return true; + case IGNORE: + return false; + default: + throw new Error("New UnknownFlagBehavior added without corresponding case clauses"); + } + } else if (flagSplit.length == 2) { + args.insertArg(flagSplit[1]); + } + + element.parse(source, args, context); + return true; + } + + private boolean parseShortFlags(PermissibleCommandSource source, String shortFlags, CommandArgs args, CommandContext context) throws ArgumentParseException { + for (int i = 0; i < shortFlags.length(); i++) { + String shortFlag = shortFlags.substring(i, i + 1); + CommandElement element = this.shortFlags.get(shortFlag); + + if (element == null) { + switch (this.unknownShortFlagBehavior) { + case IGNORE: + if (i == 0) { + return false; + } + + throw args.createError(ChatMessage.createTextMessage(String.format("Unknown short flag %s specified", shortFlag))); + case ERROR: + throw args.createError(ChatMessage.createTextMessage(String.format("Unknown short flag %s specified", shortFlag))); + case ACCEPT_NONVALUE: + context.addFlag(shortFlag); + context.putArg(shortFlag, true); + break; + case ACCEPT_VALUE: + context.addFlag(shortFlag); + context.putArg(shortFlag, args.next()); + break; + default: + throw new Error("New UnknownFlagBehavior added without corresponding case clauses"); + } + } else { + element.parse(source, args, context); + } + } + + return true; + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + final List builder = new ArrayList<>(); + + for (Map.Entry, CommandElement> arg : this.usageFlags.entrySet()) { + builder.add("["); + + for (Iterator it = arg.getKey().iterator(); it.hasNext(); ) { + String flag = it.next(); + builder.add(flag.length() > 1 ? "--" : "-"); + builder.add(flag); + + if (it.hasNext()) { + builder.add("|"); + } + } + + ChatMessage usage = arg.getValue().getUsage(src); + + if (usage.toString().trim().length() > 0) { + builder.add(" "); + builder.add(usage.toString()); + } + + builder.add("]"); + builder.add(" "); + } + + if (this.childElement != null) { + builder.add(this.childElement.getUsage(src)); + } + + return ChatMessage.createTextMessage(Arrays.stream(builder.toArray()).map(Object::toString).collect(Collectors.joining())); + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return null; + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + CommandArgs.Snapshot state = args.getSnapshot(); + + while (args.hasNext()) { + String next = args.nextIfPresent().get(); + + if (next.startsWith("-")) { + CommandArgs.Snapshot start = args.getSnapshot(); + List ret; + + if (next.startsWith("--")) { + ret = this.tabCompleteLongFlag(next.substring(2), src, args, context); + } else { + ret = this.tabCompleteShortFlags(next.substring(1), src, args, context); + } + + if (ret != null) { + return ret; + } + + args.removeArgs(start, args.getSnapshot()); + } else if (this.anchorFlags) { + break; + } + } + + // the modifications are intentional + args.applySnapshot(state, false); + + // Prevent tab completion gobbling up an argument if the value parsed. + if (!args.hasNext() && !args.getRaw().matches("\\s+$")) { + return ImmutableList.of(); + } + + return this.childElement != null ? this.childElement.complete(src, args, context) : ImmutableList.of(); + } + + @Nullable + private List tabCompleteLongFlag(String longFlag, PermissibleCommandSource src, CommandArgs args, CommandContext context) { + String[] flagSplit = longFlag.split("=", 2); + boolean isSplitFlag = flagSplit.length == 2; + CommandElement element = this.longFlags.get(flagSplit[0].toLowerCase()); + + if (element == null || !isSplitFlag && !args.hasNext()) { + return this.longFlags.keySet().stream() + .filter((input) -> input.startsWith(flagSplit[0])) + .map(f -> "--" + f) + .collect(Collectors.toList()); + } else if (isSplitFlag) { + args.insertArg(flagSplit[1]); + } + + CommandArgs.Snapshot state = args.getSnapshot(); + List completion; + + try { + element.parse(src, args, context); + + if (args.getSnapshot().equals(state)) { + // Not iterated, but succeeded. Check completions to account for optionals + completion = element.complete(src, args, context); + } else { + args.previous(); + String res = args.peek(); + completion = element.complete(src, args, context); + + if (!completion.contains(res)) { + completion = ImmutableList.builder().addAll(completion).add(res).build(); + } + } + } catch (ArgumentParseException ex) { + args.applySnapshot(state); + completion = element.complete(src, args, context); + } + + if (completion.isEmpty()) { + if (isSplitFlag) { + return ImmutableList.of(); // so we don't overwrite the flag + } + + return null; + } + + if (isSplitFlag) { + return completion.stream().map(x -> "--" + flagSplit[0] + "=" + x).collect(Collectors.toList()); + } + + return completion; + } + + @Nullable + private List tabCompleteShortFlags(String shortFlags, PermissibleCommandSource src, CommandArgs args, CommandContext context) { + for (int i = 0; i < shortFlags.length(); i++) { + CommandElement element = this.shortFlags.get(shortFlags.substring(i, i + 1)); + + if (element == null) { + if (i == 0 && this.unknownShortFlagBehavior == UnknownFlagBehavior.ACCEPT_VALUE) { + args.nextIfPresent(); + return null; + } + } else { + CommandArgs.Snapshot start = args.getSnapshot(); + + try { + element.parse(src, args, context); + + // if the iterator hasn't moved, then just try to complete, no point going backwards. + if (args.getSnapshot().equals(start)) { + return element.complete(src, args, context); + } + + // if we managed to parse this, then go back to get the completions for it. + args.previous(); + String currentText = args.peek(); + + // ensure this is returned as a valid option + List elements = element.complete(src, args, context); + + if (!elements.contains(currentText)) { + return ImmutableList.builder().add(args.peek()).addAll(element.complete(src, args, context)).build(); + } else { + return elements; + } + } catch (ArgumentParseException ex) { + args.applySnapshot(start); + return element.complete(src, args, context); + } + } + } + + return null; + } + + /** + * Indicates to the flag parser how it should treat an argument that looks + * like a flag that it does not recognise. + */ + public enum UnknownFlagBehavior { + /** + * Throw an {@link ArgumentParseException} when an unknown flag is + * encountered. + */ + ERROR, + /** + * Mark the flag as a non-value flag. + */ + ACCEPT_NONVALUE, + + /** + * Mark the flag as a string-valued flag. + */ + ACCEPT_VALUE, + /** + * Act as if the unknown flag is an ordinary argument, allowing the + * parsers specified in {@link Builder#buildWith(CommandElement)} to + * attempt to parse the element instead. + */ + IGNORE, + } + + public static class Builder { + private final Map, CommandElement> usageFlags = new HashMap<>(); + private final Map shortFlags = new HashMap<>(); + private final Map longFlags = new HashMap<>(); + private UnknownFlagBehavior unknownLongFlagBehavior = UnknownFlagBehavior.ERROR; + private UnknownFlagBehavior unknownShortFlagBehavior = UnknownFlagBehavior.ERROR; + private boolean anchorFlags = false; + + Builder() { + } + + private Builder flag(Function func, String... specs) { + final List availableFlags = new ArrayList<>(specs.length); + CommandElement el = null; + + for (String spec : specs) { + if (spec.startsWith("-")) { + final String flagKey = spec.substring(1); + + if (el == null) { + el = func.apply(flagKey); + } + + availableFlags.add(flagKey); + this.longFlags.put(flagKey.toLowerCase(), el); + } else { + for (int i = 0; i < spec.length(); ++i) { + final String flagKey = spec.substring(i, i + 1); + + if (el == null) { + el = func.apply(flagKey); + } + + availableFlags.add(flagKey); + this.shortFlags.put(flagKey, el); + } + } + } + + this.usageFlags.put(availableFlags, el); + return this; + } + + /** + * Allow a flag with any of the provided specifications that has no + * value. This flag will be exposed in a {@link CommandContext} under + * the key equivalent to the first flag in the specification array. + * The specifications are handled as so for each element in the + * {@code specs} array: + *
    + *
  • If the element starts with -, the remainder of the element + * is interpreted as a long flag (so, "-flag" means "--flag" will + * be matched in an argument string)
  • + *
  • Otherwise, each code point of the element is interpreted + * as a short flag (meaning "flag" will cause "-f", "-l", "-a" and + * "-g" to be matched in an argument string, storing "true" under + * the key "f".)
  • + *
+ * + * @param specs The flag specifications + * @return this + */ + public Builder flag(String... specs) { + return this.flag(input -> new FlagElement(ChatMessage.createTextMessage(input), null), specs); + } + + /** + * Allow a flag with any of the provided specifications that has no + * value but requires the source to have a specific permission to + * specify the command. + * + * @param flagPermission The required permission + * @param specs The flag specifications + * @return this + * @see #flag(String...) for details on the format + */ + public Builder permissionFlag(final String flagPermission, String... specs) { + return this.flag(input -> GenericArguments.requiringPermission(new FlagElement(ChatMessage.createTextMessage(input), null), flagPermission), specs); + } + + /** + * Allow a flag with any of the provided specifications, with the given + * command element. The flag may be present multiple times, and may + * therefore have multiple values. + * + * @param value The command element used to parse any occurrences + * @param specs The flag specifications + * @return this + * @see #flag(String...) for information on how the flag specifications + * are parsed + */ + public Builder valueFlag(CommandElement value, String... specs) { + return this.flag(input -> new FlagElement(ChatMessage.createTextMessage(input), value), specs); + } + + /** + * Sets how long flags that are not registered should be handled when + * encountered. + * + * @param behavior The behavior to use + * @return this + */ + public Builder setUnknownLongFlagBehavior(UnknownFlagBehavior behavior) { + this.unknownLongFlagBehavior = behavior; + return this; + } + + /** + * Sets how long flags that are not registered should be handled when + * encountered. + * + *

If a command that supports flags accepts negative numbers (or + * arguments that may begin with a dash), setting this to + * {@link UnknownFlagBehavior#IGNORE} will cause these elements to + * be ignored by the flag parser and will be parsed by the command's + * non-flag elements instead.

+ * + * @param behavior The behavior to use + * @return this + */ + public Builder setUnknownShortFlagBehavior(UnknownFlagBehavior behavior) { + this.unknownShortFlagBehavior = behavior; + return this; + } + + /** + * Whether flags should be anchored to the beginning of the text (so + * flags will only be picked up if they are at the beginning of the + * input). + * + * @param anchorFlags Whether flags are anchored + * @return this + */ + public Builder setAnchorFlags(boolean anchorFlags) { + this.anchorFlags = anchorFlags; + return this; + } + + /** + * Build a flag command element using the given command element to + * handle all non-flag arguments. + * + *

If you wish to add multiple elements here, wrap them in + * {@link GenericArguments#seq(CommandElement...)}

+ * + * @param wrapped The wrapped command element + * @return the new command element + */ + public CommandElement buildWith(CommandElement wrapped) { + return new CommandFlags(wrapped, this.usageFlags, this.shortFlags, this.longFlags, this.unknownShortFlagBehavior, this.unknownLongFlagBehavior, this.anchorFlags); + } + } + + private static class FlagElement extends CommandElement { + @Nullable + private final CommandElement valueElement; + + private FlagElement(ChatMessage key, @Nullable CommandElement valueElement) { + super(key); + this.valueElement = valueElement; + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + String key = this.getUntranslatedKey(); + + if (this.valueElement != null) { + this.valueElement.parse(source, args, context); + } else { + context.putArg(key, true); + } + + context.addFlag(key); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return null; //unused + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + return this.valueElement != null ? this.valueElement.complete(src, args, context) : Collections.emptyList(); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/GenericArguments.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/GenericArguments.java new file mode 100644 index 000000000..e05441e77 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/GenericArguments.java @@ -0,0 +1,957 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.URL; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Collection; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.text.ChatMessage; +import net.minecraft.util.math.Vec3d; + +import net.fabricmc.loader.api.ModContainer; + +import net.legacyfabric.fabric.api.command.v2.StringType; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; +import net.legacyfabric.fabric.api.util.TriState; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.AllOfCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.BigDecimalElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.BigIntegerElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.ChoicesCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.DateTimeElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.DurationElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.EntityCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.EnumValueElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.FilteredSuggestionsElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.FirstParsingCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.IpElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.LiteralCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.MarkTrueCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.ModCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.NumericElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.OnlyOneCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.OptionalCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.PermissionCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.PlayerCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.RemainingJoinedStringsCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.RepeatedCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.SequenceCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.StringCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.StringElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.UrlElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.UuidElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.Vec3dCommandElement; +import net.legacyfabric.fabric.impl.command.lib.sponge.args.WithSuggestionsElement; + +/** + * Class containing factory methods for common command elements. + */ +@SuppressWarnings({"UnstableApiUsage"}) +public class GenericArguments { + private static final CommandElement NONE = new SequenceCommandElement(ImmutableList.of()); + + private GenericArguments() { + } + + /** + * Expects no arguments, returns no values. + * + * @return An expectation of no arguments + */ + public static CommandElement none() { + return NONE; + } + + /** + * Expects no arguments. Adds 'true' to the context when parsed. + * + *

This will return only one value.

+ * + * @param key the key to store 'true' under + * @return the argument + */ + public static CommandElement markTrue(ChatMessage key) { + return new MarkTrueCommandElement(key); + } + + /** + * Expect an argument to represent online players, or if nothing matches + * and the source is a {@link PlayerEntity}, give the player. If nothing matches + * and the source is not a player, throw an exception. + * + *

Gives values of type {@link PlayerEntity}.

+ * + *

This argument accepts the following inputs:

+ * + *
    + *
  • A player's name
  • + *
  • A regex that matches the beginning of one or more player's names + *
  • + *
  • A selector
  • + *
+ * + *

This may return multiple players. If you must only return one, wrap + * this element in an {@link #onlyOne(CommandElement)} call.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement playerOrSource(ChatMessage key) { + return new PlayerCommandElement(key, true); + } + + /** + * Expect an argument to represent online players. Returns values of type + * {@link PlayerEntity}. + * + *

This argument accepts the following inputs:

+ * + *
    + *
  • A player's name
  • + *
  • A regex that matches the beginning of one or more player's names + *
  • + *
  • A selector
  • + *
+ * + *

This may return multiple players. If you must only return one, wrap + * this element in an {@link #onlyOne(CommandElement)} call.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement player(ChatMessage key) { + return new PlayerCommandElement(key, false); + } + + /** + * Expect an argument to represent a {@link Vec3d}. + * + *

This will return one value.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement vec3d(ChatMessage key) { + return new Vec3dCommandElement(key); + } + + /** + * Expect an argument to represent a {@link ModContainer}'s id. + * + *

This argument accepts the following inputs:

+ * + *
    + *
  • The specified {@link ModContainer}'s id
  • + *
  • A regex that matches the beginning of one or more plugin id
  • + *
+ * + *

This may return multiple {@link ModContainer}s. If you must only + * return one, wrap this element in an {@link #onlyOne(CommandElement)} + * call.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement mod(ChatMessage key) { + return new ModCommandElement(key); + } + + /** + * Gets a builder to create a command element that parses flags. + * + *

There should only be ONE of these in a command element sequence if you + * wish to use flags. A {@link CommandFlags.Builder} can handle multiple + * flags that have different behaviors. Using multiple builders in the same + * sequence may cause unexpected behavior.

+ * + *

Any command elements that are not associated with flags should be + * placed into the {@link CommandFlags.Builder#buildWith(CommandElement)} + * parameter, allowing the flags to be used throughout the argument string. + *

+ * + * @return the newly created builder + */ + public static CommandFlags.Builder flags() { + return new CommandFlags.Builder(); + } + + /** + * Consumes a series of arguments. Usage is the elements concatenated + * + * @param elements The series of arguments to expect + * @return the element to match the input + */ + public static CommandElement seq(CommandElement... elements) { + return new SequenceCommandElement(ImmutableList.copyOf(elements)); + } + + /** + * Return an argument that allows selecting from a limited set of values. + * + *

If there are 5 or fewer choices available, the choices will be shown + * in the command usage. Otherwise, the usage will only display only the + * key.

+ * + *

Choices are case sensitive. If you do not require + * case sensitivity, see {@link #choicesInsensitive(Text, Map)}.

+ * + *

To override this behavior, see + * {@link #choices(Text, Map, boolean, boolean)}.

+ * + *

When parsing, only one choice may be selected, returning its + * associated value.

+ * + * @param key The key to store the resulting value under + * @param choices The choices users can choose from + * @return the element to match the input + */ + public static CommandElement choices(ChatMessage key, Map choices) { + return choices(key, choices, choices.size() <= ChoicesCommandElement.CUTOFF, true); + } + + /** + * Return an argument that allows selecting from a limited set of values. + * + *

If there are 5 or fewer choices available, the choices will be shown + * in the command usage. Otherwise, the usage will only display only the + * key.

+ * + *

Choices are not case sensitive. If you require + * case sensitivity, see {@link #choices(Text, Map)}

+ * + *

To override this behavior, see + * {@link #choices(Text, Map, boolean, boolean)}.

+ * + *

When parsing, only one choice may be selected, returning its + * associated value.

+ * + * @param key The key to store the resulting value under + * @param choices The choices users can choose from + * @return the element to match the input + */ + public static CommandElement choicesInsensitive(ChatMessage key, Map choices) { + return choices(key, choices, choices.size() <= ChoicesCommandElement.CUTOFF, false); + } + + /** + * Return an argument that allows selecting from a limited set of values. + * + *

Unless {@code choicesInUsage} is true, general command usage will only + * display the provided key.

+ * + *

Choices are case sensitive. If you do not require + * case sensitivity, see {@link #choices(Text, Map, boolean, boolean)}

+ * + *

When parsing, only one choice may be selected, returning its + * associated value.

+ * + * @param key The key to store the resulting value under + * @param choices The choices users can choose from + * @param choicesInUsage Whether to display the available choices, or simply + * the provided key, as part of usage + * @return the element to match the input + */ + public static CommandElement choices(ChatMessage key, Map choices, boolean choicesInUsage) { + return choices(key, choices, choicesInUsage, true); + } + + /** + * Return an argument that allows selecting from a limited set of values. + * + *

Unless {@code choicesInUsage} is true, general command usage will only + * display the provided key.

+ * + *

When parsing, only one choice may be selected, returning its + * associated value.

+ * + * @param key The key to store the resulting value under + * @param choices The choices users can choose from + * @param choicesInUsage Whether to display the available choices, or simply + * the provided key, as part of usage + * @param caseSensitive Whether the matches should be case sensitive + * @return the element to match the input + */ + public static CommandElement choices(ChatMessage key, Map choices, boolean choicesInUsage, boolean caseSensitive) { + if (!caseSensitive) { + Map choicesMap = choices.entrySet().stream().collect(Collectors.toMap(x -> x.getKey().toLowerCase(), Map.Entry::getValue)); + return choices(key, choicesMap::keySet, selection -> choicesMap.get(selection.toLowerCase()), choicesInUsage); + } + + Map immChoices = ImmutableMap.copyOf(choices); + return choices(key, immChoices::keySet, immChoices::get, choicesInUsage); + } + + /** + * Return an argument that allows selecting from a limited set of values. + * + *

If there are 5 or fewer choices available, the choices will be shown + * in the command usage. Otherwise, the usage will only display only the + * key.

+ * + *

To override this behavior, see {@link #choices(Text, Map, boolean)}. + *

+ * + *

Only one choice may be selected, returning its associated value.

+ * + * @param key The key to store the resulting value under + * @param keys The function that will supply available keys + * @param values The function that maps an element of {@code key} to a value + * and any other key to {@code null} + * @return the element to match the input + */ + public static CommandElement choices(ChatMessage key, Supplier> keys, Function values) { + return new ChoicesCommandElement(key, keys, values, TriState.DEFAULT); + } + + /** + * Return an argument that allows selecting from a limited set of values. + * Unless {@code choicesInUsage} is true, general command usage will only + * display the provided key. + * + *

Only one choice may be selected, returning its associated value.

+ * + * @param key The key to store the resulting value under + * @param keys The function that will supply available keys + * @param values The function that maps an element of {@code key} to a value + * and any other key to {@code null} + * @param choicesInUsage Whether to display the available choices, or simply + * the provided key, as part of usage + * @return the element to match the input + */ + public static CommandElement choices(ChatMessage key, Supplier> keys, Function values, boolean choicesInUsage) { + return new ChoicesCommandElement(key, keys, values, choicesInUsage ? TriState.TRUE : TriState.FALSE); + } + + /** + * Returns a command element that matches the first of the provided elements + * that parses tab completion matches from all options. + * + * @param elements The elements to check against + * @return The command element matching the first passing of the elements + * provided + */ + public static CommandElement firstParsing(CommandElement... elements) { + return new FirstParsingCommandElement(ImmutableList.copyOf(elements)); + } + + /** + * Make the provided command element optional. + * + *

This means the command element is not required. However, if the + * element is provided with invalid format and there are no more args + * specified, any errors will still be passed on.

+ * + * @param element The element to optionally require + * @return the element to match the input + */ + public static CommandElement optional(CommandElement element) { + return new OptionalCommandElement(element, null, false); + } + + /** + * Make the provided command element optional. + * + *

This means the command element is not required. However, if the + * element is provided with invalid format and there are no more args + * specified, any errors will still be passed on. If the given element's key + * and {@code value} are not null and this element is not provided the + * element's key will be set to the given value.

+ * + * @param element The element to optionally require + * @param value The default value to set + * @return the element to match the input + */ + public static CommandElement optional(CommandElement element, Object value) { + return new OptionalCommandElement(element, value, false); + } + + /** + * Make the provided command element optional + * This means the command element is not required. + * If the argument is provided but of invalid format, it will be skipped. + * + * @param element The element to optionally require + * @return the element to match the input + */ + public static CommandElement optionalWeak(CommandElement element) { + return new OptionalCommandElement(element, null, true); + } + + /** + *

Make the provided command element optional.

+ * + *

This means the command element is not required.

+ * + *
    + *
  • If the argument is provided but of invalid format, it will be + * skipped.
  • + *
  • If the given element's key and {@code value} are not null and + * this element is not provided the element's key will be set to the + * given value.
  • + *
+ * + * @param element The element to optionally require + * @param value The default value to set + * @return the element to match the input + */ + public static CommandElement optionalWeak(CommandElement element, Object value) { + return new OptionalCommandElement(element, value, true); + } + + /** + * Require a given command element to be provided a certain number of times. + * + *

Command values will be stored under their provided keys in the + * CommandContext.

+ * + * @param element The element to repeat + * @param times The number of times to repeat the element. + * @return the element to match the input + */ + public static CommandElement repeated(CommandElement element, int times) { + return new RepeatedCommandElement(element, times); + } + + /** + * Require all remaining args to match as many instances of + * {@link CommandElement} as will fit. Command element values will be stored + * under their provided keys in the CommandContext. + * + * @param element The element to repeat + * @return the element to match the input + */ + public static CommandElement allOf(CommandElement element) { + return new AllOfCommandElement(element); + } + + // -- Argument types for basic java types + + /** + * Require an argument to be a string. Any provided argument will fit in + * under this argument. + * + *

Gives values of type {@link String}. This will return only one value. + *

+ * + * @param key The key to store the parsed argument under + * @return the element to match the input + */ + public static CommandElement string(ChatMessage key) { + return new StringElement(key); + } + + /** + * Require an argument to be an integer (base 10). + * + *

Gives values of type {@link Integer}. This will return only one value. + *

+ * + * @param key The key to store the parsed argument under + * @return the element to match the input + */ + public static CommandElement integer(ChatMessage key) { + return new NumericElement<>(key, Integer::parseInt, Integer::parseInt, input -> ChatMessage.createTextMessage(String.format("Expected an integer, but input '%s' was not", input))); + } + + /** + * Require an argument to be a long (base 10). + * + *

Gives values of type {@link Long}. This will return only one value. + *

+ * + * @param key The key to store the parsed argument under + * @return the element to match the input + */ + public static CommandElement longNum(ChatMessage key) { + return new NumericElement<>(key, Long::parseLong, Long::parseLong, input -> ChatMessage.createTextMessage(String.format("Expected a long, but input '%s' was not", input))); + } + + /** + * Require an argument to be an double-precision floating point number. + * + *

Gives values of type {@link Double}. This will return only one value. + *

+ * + * @param key The key to store the parsed argument under + * @return the element to match the input + */ + public static CommandElement doubleNum(ChatMessage key) { + return new NumericElement<>(key, Double::parseDouble, null, input -> ChatMessage.createTextMessage(String.format("Expected a number, but input '%s' was not", input))); + } + + private static final Map BOOLEAN_CHOICES = ImmutableMap.builder() + .put("true", true) + .put("t", true) + .put("y", true) + .put("yes", true) + .put("verymuchso", true) + .put("1", true) + .put("false", false) + .put("f", false) + .put("n", false) + .put("no", false) + .put("notatall", false) + .put("0", false) + .build(); + + /** + * Require an argument to be a boolean. + * + *

The recognized true values are:

+ * + *
    + *
  • true
  • + *
  • t
  • + *
  • yes
  • + *
  • y
  • + *
  • verymuchso
  • + *
+ * + * + *

The recognized false values are:

+ * + *
    + *
  • false
  • + *
  • f
  • + *
  • no
  • + *
  • n
  • + *
  • notatall
  • + *
+ * + *

Gives values of type {@link Boolean}. This will return only one value. + *

+ * + * @param key The key to store the parsed argument under + * @return the element to match the input + */ + public static CommandElement bool(ChatMessage key) { + return GenericArguments.choices(key, BOOLEAN_CHOICES); + } + + /** + * Require the argument to be a key under the provided enum. + * + *

Gives values of type T. This will return only one value.

+ * + * @param key The key to store the matched enum value under + * @param type The enum class to get enum constants from + * @param The type of enum + * @return the element to match the input + */ + public static > CommandElement enumValue(ChatMessage key, Class type) { + return new EnumValueElement<>(key, type); + } + + /** + * Require one or more strings, which are combined into a single, + * space-separated string. + * + *

Gives values of type {@link String}. This will return only one value. + *

+ * + * @param key The key to store the parsed argument under + * @return the element to match the input + */ + public static CommandElement remainingJoinedStrings(ChatMessage key) { + return new RemainingJoinedStringsCommandElement(key, false); + } + + /** + * Require one or more strings, without any processing, which are combined + * into a single, space-separated string. + * + *

Gives values of type {@link String}. This will return only one value. + *

+ * + * @param key The key to store the parsed argument under + * @return the element to match the input + */ + public static CommandElement remainingRawJoinedStrings(ChatMessage key) { + return new RemainingJoinedStringsCommandElement(key, true); + } + + /** + * Expect a literal sequence of arguments. This element matches the input + * against a predefined array of arguments expected to be present, + * case-insensitively. + * + *

This will return only one value.

+ * + * @param key The key to add to the context. Will be set to a value of true + * if this element matches + * @param expectedArgs The sequence of arguments expected + * @return the appropriate command element + */ + public static CommandElement literal(ChatMessage key, String... expectedArgs) { + return new LiteralCommandElement(key, ImmutableList.copyOf(expectedArgs), true); + } + + /** + * Expect a literal sequence of arguments. This element matches the input + * against a predefined array of arguments expected to be present, + * case-insensitively. + * + *

This will return only one value.

+ * + * @param key The key to store this argument as + * @param putValue The value to put at key if this argument matches. May be + * null + * @param expectedArgs The sequence of arguments expected + * @return the appropriate command element + */ + public static CommandElement literal(ChatMessage key, @Nullable Object putValue, String... expectedArgs) { + return new LiteralCommandElement(key, ImmutableList.copyOf(expectedArgs), putValue); + } + + /** + * Restricts the given command element to only insert one value into the + * context at the provided key. + * + *

If more than one value is returned by an element, or the target key + * already contains a value, this will throw an + * {@link ArgumentParseException}

+ * + * @param element The element to restrict + * @return the restricted element + */ + public static CommandElement onlyOne(CommandElement element) { + return new OnlyOneCommandElement(element); + } + + /** + * Checks a permission for a given command argument to be used. + * + *

If the element attempts to parse an argument and the user does not + * have the permission, an {@link ArgumentParseException} will be thrown.

+ * + * @param element The element to wrap + * @param permission The permission to check + * @return the element + */ + public static CommandElement requiringPermission(CommandElement element, String permission) { + return new PermissionCommandElement(element, permission, false); + } + + /** + * Checks a permission for a given command argument to be used. + * + *

If the element attempts to parse an argument and the user does not + * have the permission, the element will be skipped over.

+ * + *

If the invoking {@link PermissibleCommandSource} has permission to use the + * element, but the wrapped element fails to parse, an exception will + * be reported in the normal way. If you require this element to be + * truly optional, wrap this element in either + * {@link #optional(CommandElement)} or + * {@link #optionalWeak(CommandElement)}, as required.

+ * + * @param element The element to wrap + * @param permission The permission to check + * @return the element + */ + public static CommandElement requiringPermissionWeak(CommandElement element, String permission) { + return new PermissionCommandElement(element, permission, true); + } + + /** + * Expect an argument to represent an {@link Entity}. + * + *

This argument accepts the following inputs:

+ * + *
    + *
  • A player's name
  • + *
  • An entity's {@link UUID}
  • + *
  • A regex that matches the beginning of one or more player's names + * or entities UUIDs. + *
  • + *
  • A selector
  • + *
+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement entity(ChatMessage key) { + return new EntityCommandElement(key, false, false, null); + } + + /** + * Expect an argument to represent an {@link Entity} of the specified type. + * + *

This argument accepts the following inputs:

+ * + *
    + *
  • A player's name (if appropriate)
  • + *
  • An entity's {@link UUID}
  • + *
  • A regex that matches the beginning of one or more player's names + * or entities UUIDs. + *
  • + *
  • A selector
  • + *
+ * + * @param key The key to store under + * @param clazz The type which the entity must subclass + * @return the argument + */ + public static CommandElement entity(ChatMessage key, Class clazz) { + return new EntityCommandElement(key, false, false, clazz); + } + + /** + * Expect an argument to represent an {@link Entity} of the specified type, + * or if the argument is not present and the {@link PermissibleCommandSource} is + * looking at an applicable entity, return that entity. + * + *

This argument accepts the following inputs:

+ * + *
    + *
  • A player's name (if appropriate)
  • + *
  • An entity's {@link UUID}
  • + *
  • A regex that matches the beginning of one or more player's names + * or entities UUIDs. + *
  • + *
  • A selector
  • + *
+ * + * @param key The key to store under + * @param clazz The type which the entity must subclass + * @return the argument + */ + public static CommandElement entityOrTarget(ChatMessage key, Class clazz) { + return new EntityCommandElement(key, false, true, clazz); + } + + /** + * Expect an argument to represent a {@link URL}. + * + *

This will return only one value.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement url(ChatMessage key) { + return new UrlElement(key); + } + + /** + * Expect an argument to return an IP address, in the form of an + * {@link InetAddress}. + * + *

This will return only one value.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement ip(ChatMessage key) { + return new IpElement(key); + } + + /** + * Expect an argument to return a {@link BigDecimal}. + * + * @param key The key to store under + * @return the argument + */ + public static CommandElement bigDecimal(ChatMessage key) { + return new BigDecimalElement(key); + } + + /** + * Expect an argument to return a {@link BigInteger}. + * + *

This will return only one value.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement bigInteger(ChatMessage key) { + return new BigIntegerElement(key); + } + + /** + * Expect an argument to be a {@link UUID}. + * + *

This will return only one value.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement uuid(ChatMessage key) { + return new UuidElement(key); + } + + /** + * Expect an argument to be a {@link String}. + * + *

This will return only one value.

+ * + * @param key The key to store under + * @param type The type of parsing to be followed + * @return the argument + */ + public static CommandElement string(ChatMessage key, StringType type) { + return new StringCommandElement(key, type); + } + + /** + * Expect an argument to be a date-time, in the form of a + * {@link LocalDateTime}. If no date is specified, {@link LocalDate#now()} + * is used; if no time is specified, {@link LocalTime#MIDNIGHT} is used. + * + *

Date-times are expected in the ISO-8601 format.

+ * + *

This will return only one value.

+ * + * @param key The key to store under + * @return the argument + * @see ISO-8601 + */ + public static CommandElement dateTime(ChatMessage key) { + return new DateTimeElement(key, false); + } + + /** + * Expect an argument to be a date-time, in the form of a + * {@link LocalDateTime}. If no date is specified, {@link LocalDate#now()} + * is used; if no time is specified, {@link LocalTime#MIDNIGHT} is used. + * + *

If no argument at all is specified, defaults to + * {@link LocalDateTime#now()}.

+ * + *

Date-times are expected in the ISO-8601 format.

+ * + *

This will return only one value.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement dateTimeOrNow(ChatMessage key) { + return new DateTimeElement(key, true); + } + + /** + * Expect an argument to be a {@link Duration}. + * + *

Durations are expected in the following format: {@code #D#H#M#S}. + * This is not case sensitive.

+ * + *

This will return only one value.

+ * + * @param key The key to store under + * @return the argument + */ + public static CommandElement duration(ChatMessage key) { + return new DurationElement(key); + } + + /** + * Uses a custom set of suggestions for an argument. The provided + * suggestions will replace the regular ones. + * + * @param argument The element to replace the suggestions of + * @param suggestions The suggestions to use + * @return the argument + */ + public static CommandElement withSuggestions(CommandElement argument, Iterable suggestions) { + return withSuggestions(argument, suggestions, true); + } + + /** + * Uses a custom set of suggestions for an argument. The provided + * suggestions will replace the regular ones. + * + *

If {@code requireBegin} is false, then the already typed argument + * will not be used to filter the provided suggestions.

+ * + * @param argument The element to replace the suggestions of + * @param suggestions The suggestions to use + * @param requireBegin Whether or not to require the current argument to + * begin provided arguments + * @return the argument + */ + public static CommandElement withSuggestions(CommandElement argument, Iterable suggestions, boolean requireBegin) { + return withSuggestions(argument, (s) -> suggestions, requireBegin); + } + + /** + * Uses a custom set of suggestions for an argument. The provided + * suggestions will replace the regular ones. + * + * @param argument The element to replace the suggestions of + * @param suggestions A function to return the suggestions to use + * @return the argument + */ + public static CommandElement withSuggestions(CommandElement argument, Function> suggestions) { + return withSuggestions(argument, suggestions, true); + } + + /** + * Uses a custom set of suggestions for an argument. The provided + * suggestions will replace the regular ones. + * + *

If {@code requireBegin} is false, then the already typed argument + * will not be used to filter the provided suggestions.

+ * + * @param argument The element to replace the suggestions of + * @param suggestions A function to return the suggestions to use + * @param requireBegin Whether or not to require the current argument to + * begin provided arguments + * @return the argument + */ + public static CommandElement withSuggestions(CommandElement argument, Function> suggestions, boolean requireBegin) { + return new WithSuggestionsElement(argument, suggestions, requireBegin); + } + + /** + * Filters an argument's suggestions. A suggestion will only be used if it + * matches the predicate. + * + * @param argument The element to filter the suggestions of + * @param predicate The predicate to test suggestions against + * @return the argument + */ + public static CommandElement withConstrainedSuggestions(CommandElement argument, Predicate predicate) { + return new FilteredSuggestionsElement(argument, predicate); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/KeyElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/KeyElement.java new file mode 100644 index 000000000..af9b5307c --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/KeyElement.java @@ -0,0 +1,48 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +/** + * Parent class that specifies elements as having no tab completions. + * Useful for inputs with a very large domain, like strings and integers. + */ +public abstract class KeyElement extends CommandElement { + protected KeyElement(ChatMessage key) { + super(key); + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + return Collections.emptyList(); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/PatternMatchingCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/PatternMatchingCommandElement.java new file mode 100644 index 000000000..c184a86b2 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/PatternMatchingCommandElement.java @@ -0,0 +1,150 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +/** + * Abstract command element that matches values based on a regex pattern. + */ +public abstract class PatternMatchingCommandElement extends CommandElement { + private static final ChatMessage nullKeyArg = ChatMessage.createTextMessage("argument"); + final boolean useRegex; + + /** + * @param yesIWantRegex Specify if you want to allow regex for users of + * the command element. Note that this will open up for DoS attacks. + */ + protected PatternMatchingCommandElement(@Nullable ChatMessage key, boolean yesIWantRegex) { + super(key); + this.useRegex = yesIWantRegex; + } + + protected PatternMatchingCommandElement(@Nullable ChatMessage key) { + this(key, false); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + Iterable choices = this.getChoices(source); + Iterable ret; + String arg = args.next(); + + // Check to see if we have an exact match first + Optional exactMatch = this.getExactMatch(choices, arg); + + if (exactMatch.isPresent()) { + // Return this as a collection as this can get transformed by the subclass. + return Collections.singleton(exactMatch.get()); + } + + if (this.useRegex) { + Pattern pattern = this.getFormattedPattern(arg); + ret = StreamSupport.stream(choices.spliterator(), false).filter(element -> pattern.matcher(element).find()).collect(Collectors.toList()).stream().map(this::getValue).collect(Collectors.toList()); + } else { + Iterable startsWith = StreamSupport.stream(choices.spliterator(), false).filter(element -> element.regionMatches(true, 0, arg, 0, arg.length())).collect(Collectors.toList()); + ret = StreamSupport.stream(startsWith.spliterator(), false).map(this::getValue).collect(Collectors.toList()); + } + + if (!ret.iterator().hasNext()) { + throw args.createError(ChatMessage.createTextMessage(String.format("No values matching pattern '%s' present for %s!", arg, this.getKey() == null + ? nullKeyArg : this.getKey().toString()))); + } + + return ret; + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + Iterable choices = this.getChoices(src); + final Optional nextArg = args.nextIfPresent(); + + if (nextArg.isPresent()) { + if (this.useRegex) { + choices = StreamSupport.stream(choices.spliterator(), false).filter(input -> this.getFormattedPattern(nextArg.get()).matcher(input).find()).collect(Collectors.toList()); + } else { + String arg = nextArg.get(); + choices = StreamSupport.stream(choices.spliterator(), false).filter(input -> input.regionMatches(true, 0, arg, 0, arg.length())).collect(Collectors.toList()); + } + } + + return ImmutableList.copyOf(choices); + } + + Pattern getFormattedPattern(String input) { + if (!input.startsWith("^")) { // Anchor matches to the beginning -- this lets us use find() + input = "^" + input; + } + + return Pattern.compile(input, Pattern.CASE_INSENSITIVE); + } + + /** + * Tests a string against a set of valid choices to see if it is a + * case-insensitive match. + * + * @param choices The choices available to match against + * @param potentialChoice The potential choice + * @return If matched, an {@link Optional} containing the matched value + */ + protected Optional getExactMatch(final Iterable choices, final String potentialChoice) { + return Optional.ofNullable(Iterables.tryFind(choices, potentialChoice::equalsIgnoreCase).orNull()).map(this::getValue); + } + + /** + * Gets the available choices for this command source. + * + * @param source The source requesting choices + * @return the possible choices + */ + protected abstract Iterable getChoices(PermissibleCommandSource source); + + /** + * Gets the value for a given choice. For any result in + * {@link #getChoices(PermissibleCommandSource)}, this must return a non-null value. + * Otherwise, an {@link IllegalArgumentException} may be throw. + * + * @param choice The specified choice + * @return the choice's value + * @throws IllegalArgumentException if the input string is not any return + * value of {@link #getChoices(PermissibleCommandSource)} + */ + protected abstract Object getValue(String choice) throws IllegalArgumentException; +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/SelectorCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/SelectorCommandElement.java new file mode 100644 index 000000000..41f863436 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/SelectorCommandElement.java @@ -0,0 +1,73 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args; + +import java.util.List; +import java.util.Optional; + +import com.google.common.collect.ImmutableList; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.Selector; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public abstract class SelectorCommandElement extends PatternMatchingCommandElement { + protected SelectorCommandElement(@Nullable ChatMessage key) { + super(key); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + String arg = args.peek(); + + if (arg.startsWith("@")) { // Possibly a selector + try { + return Selector.parse(args.next()).resolve(source); + } catch (IllegalArgumentException ex) { + throw args.createError(ChatMessage.createTextMessage(ex.getMessage())); + } + } + + return super.parseValue(source, args); + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + CommandArgs.Snapshot state = args.getSnapshot(); + final Optional nextArg = args.nextIfPresent(); + args.applySnapshot(state); + List choices = nextArg.isPresent() ? Selector.complete(nextArg.get()) : ImmutableList.of(); + + if (choices.isEmpty()) { + choices = super.complete(src, args, context); + } + + return choices; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/InputTokenizer.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/InputTokenizer.java new file mode 100644 index 000000000..7b838414a --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/InputTokenizer.java @@ -0,0 +1,81 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args.parsing; + +import java.util.List; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; + +public interface InputTokenizer { + /** + * Use an input string tokenizer that supports quoted arguments and + * character escapes. + * + *

Forcing lenient to true makes the following apply:

+ * + *
    + *
  • Unclosed quotations are treated as a single string from the + * opening quotation to the end of the arguments rather than throwing + * an exception
  • + *
+ * + * @param forceLenient Whether the tokenizer is forced into lenient mode + * @return the appropriate tokenizer + */ + static InputTokenizer quotedStrings(boolean forceLenient) { + return new QuotedStringTokenizer(true, forceLenient, false); + } + + /** + * Returns an input tokenizer that takes input strings and splits them by + * space. + * + * @return The appropriate tokenizer + */ + static InputTokenizer spaceSplitString() { + return SpaceSplitInputTokenizer.INSTANCE; + } + + /** + * Returns an input tokenizer that returns the input string as a single + * argument. + * + * @return The appropriate tokenizer + */ + static InputTokenizer rawInput() { + return RawStringInputTokenizer.INSTANCE; + } + + /** + * Take the input string and split it as appropriate into argument tokens. + * + * @param arguments The provided arguments + * @param lenient Whether to parse leniently + * @return The tokenized strings. Empty list if error occurs + * @throws ArgumentParseException if an invalid input is provided + */ + List tokenize(String arguments, boolean lenient) throws ArgumentParseException; +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/QuotedStringTokenizer.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/QuotedStringTokenizer.java new file mode 100644 index 000000000..127ceac79 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/QuotedStringTokenizer.java @@ -0,0 +1,172 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args.parsing; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; + +/** + * Parser for converting a quoted string into a list of arguments. + * + *

Grammar is roughly (yeah, this is not really a proper grammar but it gives + * you an idea of what's happening:

+ * + *
 WHITESPACE = Character.isWhiteSpace(codePoint)
+ * CHAR := (all unicode)
+ * ESCAPE := '\' CHAR
+ * QUOTE = ' | "
+ * UNQUOTED_ARG := (CHAR | ESCAPE)+ WHITESPACE
+ * QUOTED_ARG := QUOTE (CHAR | ESCAPE)+ QUOTE
+ * ARGS := ((UNQUOTED_ARG | QUOTED_ARG) WHITESPACE+)+
+ */ +class QuotedStringTokenizer implements InputTokenizer { + private static final int CHAR_BACKSLASH = '\\'; + private static final int CHAR_SINGLE_QUOTE = '\''; + private static final int CHAR_DOUBLE_QUOTE = '"'; + private final boolean handleQuotedStrings; + private final boolean forceLenient; + private final boolean trimTrailingSpace; + + QuotedStringTokenizer(boolean handleQuotedStrings, boolean forceLenient, boolean trimTrailingSpace) { + this.handleQuotedStrings = handleQuotedStrings; + this.forceLenient = forceLenient; + this.trimTrailingSpace = trimTrailingSpace; + } + + @Override + public List tokenize(String arguments, boolean lenient) throws ArgumentParseException { + if (arguments.length() == 0) { + return Collections.emptyList(); + } + + final TokenizerState state = new TokenizerState(arguments, lenient); + List returnedArgs = new ArrayList<>(arguments.length() / 4); + + if (this.trimTrailingSpace) { + this.skipWhiteSpace(state); + } + + while (state.hasMore()) { + if (!this.trimTrailingSpace) { + this.skipWhiteSpace(state); + } + + int startIdx = state.getIndex() + 1; + String arg = this.nextArg(state); + returnedArgs.add(new SingleArg(arg, startIdx, state.getIndex())); + + if (this.trimTrailingSpace) { + this.skipWhiteSpace(state); + } + } + + return returnedArgs; + } + + // Parsing methods + + private void skipWhiteSpace(TokenizerState state) throws ArgumentParseException { + if (!state.hasMore()) { + return; + } + + while (state.hasMore() && Character.isWhitespace(state.peek())) { + state.next(); + } + } + + private String nextArg(TokenizerState state) throws ArgumentParseException { + StringBuilder argBuilder = new StringBuilder(); + + if (state.hasMore()) { + int codePoint = state.peek(); + + if (this.handleQuotedStrings && (codePoint == CHAR_DOUBLE_QUOTE || codePoint == CHAR_SINGLE_QUOTE)) { + // quoted string + this.parseQuotedString(state, codePoint, argBuilder); + } else { + this.parseUnquotedString(state, argBuilder); + } + } + + return argBuilder.toString(); + } + + private void parseQuotedString(TokenizerState state, int startQuotation, StringBuilder builder) throws ArgumentParseException { + // Consume the start quotation character + int nextCodePoint = state.next(); + + if (nextCodePoint != startQuotation) { + throw state.createException(ChatMessage.createTextMessage(String.format("Actual next character '%c' did not match expected quotation character '%c'", + nextCodePoint, startQuotation))); + } + + while (true) { + if (!state.hasMore()) { + if (state.isLenient() || this.forceLenient) { + return; + } + + throw state.createException(ChatMessage.createTextMessage("Unterminated quoted string found")); + } + + nextCodePoint = state.peek(); + + if (nextCodePoint == startQuotation) { + state.next(); + return; + } else if (nextCodePoint == CHAR_BACKSLASH) { + this.parseEscape(state, builder); + } else { + builder.appendCodePoint(state.next()); + } + } + } + + private void parseUnquotedString(TokenizerState state, StringBuilder builder) throws ArgumentParseException { + while (state.hasMore()) { + int nextCodePoint = state.peek(); + + if (Character.isWhitespace(nextCodePoint)) { + return; + } else if (nextCodePoint == CHAR_BACKSLASH) { + this.parseEscape(state, builder); + } else { + builder.appendCodePoint(state.next()); + } + } + } + + private void parseEscape(TokenizerState state, StringBuilder builder) throws ArgumentParseException { + state.next(); // Consume \ + builder.appendCodePoint(state.next()); // TODO: Unicode character escapes (\u00A7 type thing)? + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/RawStringInputTokenizer.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/RawStringInputTokenizer.java new file mode 100644 index 000000000..fe46931e3 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/RawStringInputTokenizer.java @@ -0,0 +1,41 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args.parsing; + +import java.util.Collections; +import java.util.List; + +class RawStringInputTokenizer implements InputTokenizer { + static final RawStringInputTokenizer INSTANCE = new RawStringInputTokenizer(); + + private RawStringInputTokenizer() { + } + + @Override + public List tokenize(String arguments, boolean lenient) { + return Collections.singletonList(new SingleArg(arguments, 0, arguments.length())); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/SingleArg.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/SingleArg.java new file mode 100644 index 000000000..5b0e34ae2 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/SingleArg.java @@ -0,0 +1,110 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args.parsing; + +import com.google.common.base.Objects; + +import net.legacyfabric.fabric.impl.command.GuavaUtils; + +/** + * This represents a single argument with its start and end indexes + * in the associated raw input string. + */ +public class SingleArg { + private final String value; + private final int startIdx; + private final int endIdx; + + /** + * Create a new argument. + * + * @param value The argument string + * @param startIdx The starting index of {@code value} in an input string + * @param endIdx The ending index of {@code value} in an input string + */ + public SingleArg(String value, int startIdx, int endIdx) { + this.value = value; + this.startIdx = startIdx; + this.endIdx = endIdx; + } + + /** + * Gets the string used. + * + * @return The string used + */ + public String getValue() { + return this.value; + } + + /** + * Gets the starting index. + * + * @return The starting index + */ + public int getStartIdx() { + return this.startIdx; + } + + /** + * Gets the ending index. + * + * @return The ending index + */ + public int getEndIdx() { + return this.endIdx; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof SingleArg)) { + return false; + } + + SingleArg singleArg = (SingleArg) o; + return this.startIdx == singleArg.startIdx + && this.endIdx == singleArg.endIdx + && Objects.equal(this.value, singleArg.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(this.value, this.startIdx, this.endIdx); + } + + @Override + public String toString() { + return GuavaUtils.toStringHelper(this) + .add("value", this.value) + .add("startIdx", this.startIdx) + .add("endIdx", this.endIdx) + .toString(); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/SpaceSplitInputTokenizer.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/SpaceSplitInputTokenizer.java new file mode 100644 index 000000000..3caa7d81d --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/SpaceSplitInputTokenizer.java @@ -0,0 +1,65 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args.parsing; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import com.google.common.collect.ImmutableList; + +class SpaceSplitInputTokenizer implements InputTokenizer { + public static final SpaceSplitInputTokenizer INSTANCE = new SpaceSplitInputTokenizer(); + private static final Pattern SPACE_REGEX = Pattern.compile("^[ ]*$"); + + private SpaceSplitInputTokenizer() { + } + + @Override + public List tokenize(String arguments, boolean lenient) { + if (SPACE_REGEX.matcher(arguments).matches()) { + return ImmutableList.of(); + } + + List ret = new ArrayList<>(); + int lastIndex = 0; + int spaceIndex; + + while ((spaceIndex = arguments.indexOf(" ")) != -1) { + if (spaceIndex != 0) { + ret.add(new SingleArg(arguments.substring(0, spaceIndex), lastIndex, lastIndex + spaceIndex)); + arguments = arguments.substring(spaceIndex); + } else { + arguments = arguments.substring(1); + } + + lastIndex += spaceIndex + 1; + } + + ret.add(new SingleArg(arguments, lastIndex, lastIndex + arguments.length())); + return ret; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/TokenizerState.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/TokenizerState.java new file mode 100644 index 000000000..896393cd5 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/args/parsing/TokenizerState.java @@ -0,0 +1,74 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.args.parsing; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; + +class TokenizerState { + private final boolean lenient; + private final String buffer; + private int index = -1; + + TokenizerState(String buffer, boolean lenient) { + this.buffer = buffer; + this.lenient = lenient; + } + + // Utility methods + public boolean hasMore() { + return this.index + 1 < this.buffer.length(); + } + + public int peek() throws ArgumentParseException { + if (!this.hasMore()) { + throw this.createException(ChatMessage.createTextMessage("Buffer overrun while parsing args")); + } + + return this.buffer.codePointAt(this.index + 1); + } + + public int next() throws ArgumentParseException { + if (!this.hasMore()) { + throw this.createException(ChatMessage.createTextMessage("Buffer overrun while parsing args")); + } + + return this.buffer.codePointAt(++this.index); + } + + public ArgumentParseException createException(ChatMessage message) { + return new ArgumentParseException(message, this.buffer, this.index); + } + + public boolean isLenient() { + return this.lenient; + } + + public int getIndex() { + return this.index; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/dispatcher/Disambiguator.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/dispatcher/Disambiguator.java new file mode 100644 index 000000000..41faa40b7 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/dispatcher/Disambiguator.java @@ -0,0 +1,48 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.dispatcher; + +import java.util.List; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMapping; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +@FunctionalInterface +public interface Disambiguator { + /** + * Disambiguate an alias in cases where there are multiple command mappings + * registered for a given alias. + * + * @param source The PermissibleCommandSource executing the command, if any + * @param aliasUsed The alias input by the user + * @param availableOptions The commands registered to this alias + * @return The specific command to use + */ + Optional disambiguate(@Nullable PermissibleCommandSource source, String aliasUsed, List availableOptions); +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/dispatcher/Dispatcher.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/dispatcher/Dispatcher.java new file mode 100644 index 000000000..9dd6c3c98 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/dispatcher/Dispatcher.java @@ -0,0 +1,123 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.dispatcher; + +import java.util.Optional; +import java.util.Set; + +import com.google.common.collect.Multimap; +import org.jetbrains.annotations.Nullable; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandCallable; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMapping; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +/** + * Executes a command based on user input. + */ +public interface Dispatcher extends CommandCallable { + /** + * Gets a list of commands. Each command, regardless of how many aliases it + * may have, will only appear once in the returned set. + * + *

The returned collection cannot be modified.

+ * + * @return A list of registrations + */ + Set getCommands(); + + /** + * Gets a list of primary aliases. + * + *

The returned collection cannot be modified.

+ * + * @return A list of aliases + */ + Set getPrimaryAliases(); + + /** + * Gets a list of all the command aliases, which includes the primary alias. + * + *

A command may have more than one alias assigned to it. The returned + * collection cannot be modified.

+ * + * @return A list of aliases + */ + Set getAliases(); + + /** + * Gets the {@link CommandMapping} associated with an alias. Returns null if + * no command is named by the given alias. + * + * @param alias The alias + * @return The command mapping, if available + */ + Optional get(String alias); + + /** + * Gets the {@link CommandMapping} associated with an alias in the context + * of a given {@link PermissibleCommandSource}. Returns null if no command is named by + * the given alias. + * + * @param alias The alias to look up + * @param source The source this alias is being looked up for + * @return The command mapping, if available + */ + Optional get(String alias, @Nullable PermissibleCommandSource source); + + /** + * Gets all the {@link CommandMapping}s associated with an alias. + * + * @param alias The alias + * @return The command mappings associated with the alias + */ + Set getAll(String alias); + + /** + * Gets all commands currently registered with this dispatcher. The returned + * value is immutable. + * + * @return a multimap from alias to mapping of every registered command + */ + Multimap getAll(); + + /** + * Returns whether the dispatcher contains a registered command for the + * given alias. + * + * @param alias The alias + * @return True if a registered command exists + */ + boolean containsAlias(String alias); + + /** + * Returns whether the dispatcher contains the given mapping. + * + * @param mapping The mapping + * @return True if a mapping exists + */ + boolean containsMapping(CommandMapping mapping); +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/dispatcher/SimpleDispatcher.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/dispatcher/SimpleDispatcher.java new file mode 100644 index 000000000..299944257 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/dispatcher/SimpleDispatcher.java @@ -0,0 +1,467 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.dispatcher; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; +import net.minecraft.util.Formatting; +import net.minecraft.world.World; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandCallable; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMapping; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMessageFormatting; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandNotFoundException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandResult; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.ImmutableCommandMapping; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.InvocationCommandException; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; +import net.legacyfabric.fabric.api.util.Location; + +/** + * A simple implementation of a {@link Dispatcher}. + */ +public final class SimpleDispatcher implements Dispatcher { + /** + * This is a disambiguator function that returns the first matching command. + */ + public static final Disambiguator FIRST_DISAMBIGUATOR = (source, aliasUsed, availableOptions) -> { + for (CommandMapping mapping : availableOptions) { + if (mapping.getPrimaryAlias().toLowerCase().equals(aliasUsed.toLowerCase())) { + return Optional.of(mapping); + } + } + + return Optional.of(availableOptions.get(0)); + }; + + private final Disambiguator disambiguatorFunc; + private final ListMultimap commands = ArrayListMultimap.create(); + + /** + * Creates a basic new dispatcher. + */ + public SimpleDispatcher() { + this(FIRST_DISAMBIGUATOR); + } + + /** + * Creates a new dispatcher with a specific disambiguator. + * + * @param disambiguatorFunc Function that returns the preferred command if + * multiple exist for a given alias + */ + public SimpleDispatcher(Disambiguator disambiguatorFunc) { + this.disambiguatorFunc = disambiguatorFunc; + } + + /** + * Register a given command using the given list of aliases. + * + *

If there is a conflict with one of the aliases (i.e. that alias + * is already assigned to another command), then the alias will be skipped. + * It is possible for there to be no alias to be available out of + * the provided list of aliases, which would mean that the command would not + * be assigned to any aliases.

+ * + *

The first non-conflicted alias becomes the "primary alias."

+ * + * @param callable The command + * @param alias An array of aliases + * @return The registered command mapping, unless no aliases could be + * registered + */ + public Optional register(CommandCallable callable, String... alias) { + Preconditions.checkNotNull(alias, "alias"); + return this.register(callable, Arrays.asList(alias)); + } + + /** + * Register a given command using the given list of aliases. + * + *

If there is a conflict with one of the aliases (i.e. that alias + * is already assigned to another command), then the alias will be skipped. + * It is possible for there to be no alias to be available out of + * the provided list of aliases, which would mean that the command would not + * be assigned to any aliases.

+ * + *

The first non-conflicted alias becomes the "primary alias."

+ * + * @param callable The command + * @param aliases A list of aliases + * @return The registered command mapping, unless no aliases could be + * registered + */ + public Optional register(CommandCallable callable, List aliases) { + return this.register(callable, aliases, Function.identity()); + } + + /** + * Register a given command using a given list of aliases. + * + *

The provided callback function will be called with a list of aliases + * that are not taken (from the list of aliases that were requested) and + * it should return a list of aliases to actually register. Aliases may be + * removed, and if no aliases remain, then the command will not be + * registered. It may be possible that no aliases are available, and thus + * the callback would receive an empty list. New aliases should not be added + * to the list in the callback as this may cause + * {@link IllegalArgumentException} to be thrown.

+ * + *

The first non-conflicted alias becomes the "primary alias."

+ * + * @param callable The command + * @param aliases A list of aliases + * @param callback The callback + * @return The registered command mapping, unless no aliases could + * be registered + */ + public synchronized Optional register(CommandCallable callable, List aliases, Function, List> callback) { + Preconditions.checkNotNull(aliases, "aliases"); + Preconditions.checkNotNull(callable, "callable"); + Preconditions.checkNotNull(callback, "callback"); + + // Invoke the callback with the commands that /can/ be registered + aliases = ImmutableList.copyOf(callback.apply(aliases)); + + if (aliases.isEmpty()) { + return Optional.empty(); + } + + String primary = aliases.get(0); + List secondary = aliases.subList(1, aliases.size()); + CommandMapping mapping = new ImmutableCommandMapping(callable, primary, secondary); + + for (String alias : aliases) { + this.commands.put(alias.toLowerCase(), mapping); + } + + return Optional.of(mapping); + } + + /** + * Remove a mapping identified by the given alias. + * + * @param alias The alias + * @return The previous mapping associated with the alias, if one was found + */ + public synchronized Collection remove(String alias) { + return this.commands.removeAll(alias.toLowerCase()); + } + + /** + * Remove all mappings identified by the given aliases. + * + * @param aliases A collection of aliases + * @return Whether any were found + */ + public synchronized boolean removeAll(Collection aliases) { + Preconditions.checkNotNull(aliases, "aliases"); + + boolean found = false; + + for (Object alias : aliases) { + if (!this.commands.removeAll(alias.toString().toLowerCase()).isEmpty()) { + found = true; + } + } + + return found; + } + + /** + * Remove a command identified by the given mapping. + * + * @param mapping The mapping + * @return The previous mapping associated with the alias, if one was found + */ + public synchronized Optional removeMapping(CommandMapping mapping) { + Preconditions.checkNotNull(mapping, "mapping"); + + CommandMapping found = null; + + Iterator it = this.commands.values().iterator(); + + while (it.hasNext()) { + CommandMapping current = it.next(); + + if (current.equals(mapping)) { + it.remove(); + found = current; + } + } + + return Optional.ofNullable(found); + } + + /** + * Remove all mappings contained with the given collection. + * + * @param mappings The collection + * @return Whether the at least one command was removed + */ + public synchronized boolean removeMappings(Collection mappings) { + Preconditions.checkNotNull(mappings, "mappings"); + boolean found = false; + Iterator it = this.commands.values().iterator(); + + while (it.hasNext()) { + if (mappings.contains(it.next())) { + it.remove(); + found = true; + } + } + + return found; + } + + @Override + public synchronized Set getCommands() { + return ImmutableSet.copyOf(this.commands.values()); + } + + @Override + public synchronized Set getPrimaryAliases() { + Set aliases = new HashSet<>(); + + for (CommandMapping mapping : this.commands.values()) { + aliases.add(mapping.getPrimaryAlias()); + } + + return Collections.unmodifiableSet(aliases); + } + + @Override + public synchronized Set getAliases() { + Set aliases = new HashSet<>(); + + for (CommandMapping mapping : this.commands.values()) { + aliases.addAll(mapping.getAllAliases()); + } + + return Collections.unmodifiableSet(aliases); + } + + @Override + public Optional get(String alias) { + return this.get(alias, null); + } + + @Override + public synchronized Optional get(String alias, @Nullable PermissibleCommandSource source) { + List results = this.commands.get(alias.toLowerCase()); + Optional result = Optional.empty(); + + if (results.size() == 1) { + result = Optional.of(results.get(0)); + } else if (results.size() > 1) { + result = this.disambiguatorFunc.disambiguate(source, alias, results); + } + + if (source != null) { + result = result.filter(m -> m.getCallable().testPermission(source)); + } + + return result; + } + + @Override + public synchronized boolean containsAlias(String alias) { + return this.commands.containsKey(alias.toLowerCase()); + } + + @Override + public boolean containsMapping(CommandMapping mapping) { + Preconditions.checkNotNull(mapping, "mapping"); + + for (CommandMapping test : this.commands.values()) { + if (mapping.equals(test)) { + return true; + } + } + + return false; + } + + @Override + public CommandResult process(PermissibleCommandSource source, String commandLine) throws CommandException { + final String[] argSplit = commandLine.split(" ", 2); + Optional cmdOptional = this.get(argSplit[0], source); + + if (!cmdOptional.isPresent()) { + throw new CommandNotFoundException(ChatMessage.createTranslateMessage("commands.generic.notFound"), argSplit[0]); + } + + final String arguments = argSplit.length > 1 ? argSplit[1] : ""; + CommandMapping mapping = cmdOptional.get(); + final CommandCallable spec = mapping.getCallable(); + + try { + return spec.process(source, arguments); + } catch (CommandNotFoundException e) { + throw new CommandException(ChatMessage.createTextMessage("No such child command: " + e.getCommand())); + } catch (RuntimeException e) { + throw new InvocationCommandException(ChatMessage.createTextMessage("An unexpected error happened executing the command"), e); + } + } + + @Override + public List getSuggestions(PermissibleCommandSource src, final String arguments, @Nullable Location targetPosition) throws CommandException { + final String[] argSplit = arguments.split(" ", 2); + Optional cmdOptional = this.get(argSplit[0], src); + + if (argSplit.length == 1) { + return Collections.unmodifiableList(new ArrayList<>(this.filterCommands(src, argSplit[0]))); + } else if (!cmdOptional.isPresent()) { + return ImmutableList.of(); + } + + return cmdOptional.get().getCallable().getSuggestions(src, argSplit[1], targetPosition); + } + + @Override + public boolean testPermission(PermissibleCommandSource source) { + for (CommandMapping mapping : this.commands.values()) { + if (mapping.getCallable().testPermission(source)) { + return true; + } + } + + return false; + } + + @Override + public Optional getShortDescription(PermissibleCommandSource source) { + return Optional.empty(); + } + + @Override + public Optional getHelp(PermissibleCommandSource source) { + if (this.commands.isEmpty()) { + return Optional.empty(); + } + + ChatMessage build = ChatMessage.createTextMessage("Available commands:\n"); + + for (Iterator it = this.filterCommands(source).iterator(); it.hasNext(); ) { + final Optional mappingOpt = this.get(it.next(), source); + + if (!mappingOpt.isPresent()) { + continue; + } + + CommandMapping mapping = mappingOpt.get(); + final Optional description = mapping.getCallable().getShortDescription(source); + ChatMessage text = ChatMessage.createTextMessage(mapping.getPrimaryAlias()); + build.addUsing(text.setColor(Formatting.GREEN).setUnderlined(Boolean.TRUE) + /* Click Event not supported in this version + .setClickEvent(new ClickEvent(ClickEventAction.RUN_COMMAND, "/" + mapping.getPrimaryAlias()))) + */).addUsing(CommandMessageFormatting.SPACE_TEXT).addUsing(description.orElse(mapping.getCallable().getUsage(source))); + + if (it.hasNext()) { + build.addText("\n"); + } + } + + return Optional.of(ChatMessage.createTextMessage(build.toString())); + } + + private Set filterCommands(final PermissibleCommandSource src) { + return Multimaps.filterValues(this.commands, input -> input.getCallable().testPermission(src)).keys().elementSet(); + } + + // Filter out commands by String first + private Set filterCommands(final PermissibleCommandSource src, String start) { + Multimap map = Multimaps.filterKeys(this.commands, + input -> input != null && input.toLowerCase().startsWith(start.toLowerCase())); + return Multimaps.filterValues(map, input -> input.getCallable().testPermission(src)).keys().elementSet(); + } + + /** + * Gets the number of registered aliases. + * + * @return The number of aliases + */ + public synchronized int size() { + return this.commands.size(); + } + + @Override + public ChatMessage getUsage(final PermissibleCommandSource source) { + final StringBuilder build = new StringBuilder(); + Iterable filteredCommands = this.filterCommands(source).stream() + .filter(input -> { + if (input == null) { + return false; + } + + final Optional ret = this.get(input, source); + return ret.isPresent() && ret.get().getPrimaryAlias().equals(input); + }) + .collect(Collectors.toList()); + + for (Iterator it = filteredCommands.iterator(); it.hasNext(); ) { + build.append(it.next()); + + if (it.hasNext()) { + build.append(CommandMessageFormatting.PIPE_TEXT); + } + } + + return ChatMessage.createTextMessage(build.toString()); + } + + @Override + public synchronized Set getAll(String alias) { + return ImmutableSet.copyOf(this.commands.get(alias)); + } + + @Override + public Multimap getAll() { + return ImmutableMultimap.copyOf(this.commands); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/spec/CommandExecutor.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/spec/CommandExecutor.java new file mode 100644 index 000000000..7c39039f5 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/spec/CommandExecutor.java @@ -0,0 +1,49 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.spec; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandResult; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +/** + * Interface containing the method directing how a certain command will + * be executed. + */ +@FunctionalInterface +public interface CommandExecutor { + /** + * Callback for the execution of a command. + * + * @param src The commander who is executing this command + * @param args The parsed command arguments for this command + * @return the result of executing this command + * @throws CommandException If a user-facing error occurs while + * executing this command + */ + CommandResult execute(PermissibleCommandSource src, CommandContext args) throws CommandException; +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/spec/CommandSpec.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/spec/CommandSpec.java new file mode 100644 index 000000000..dd0728a34 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/api/command/v2/lib/sponge/spec/CommandSpec.java @@ -0,0 +1,499 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.api.command.v2.lib.sponge.spec; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; +import net.minecraft.world.World; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandCallable; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandPermissionException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandResult; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ChildCommandElementExecutor; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.GenericArguments; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.parsing.InputTokenizer; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; +import net.legacyfabric.fabric.api.util.Location; + +/** + * Specification for how command arguments should be parsed. + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public final class CommandSpec implements CommandCallable { + private final CommandElement args; + private final CommandExecutor executor; + private final Optional description; + private final Optional extendedDescription; + @Nullable + private final String permission; + private final InputTokenizer argumentParser; + + CommandSpec(CommandElement args, CommandExecutor executor, @Nullable ChatMessage description, @Nullable ChatMessage extendedDescription, + @Nullable String permission, InputTokenizer parser) { + this.args = args; + this.executor = executor; + this.permission = permission; + this.description = Optional.ofNullable(description); + this.extendedDescription = Optional.ofNullable(extendedDescription); + this.argumentParser = parser; + } + + /** + * Return a new builder for a CommandSpec. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for command specs. + */ + public static final class Builder { + private static final CommandElement DEFAULT_ARG = GenericArguments.none(); + private CommandElement args = DEFAULT_ARG; + @Nullable + private ChatMessage description; + @Nullable + private ChatMessage extendedDescription; + @Nullable + private String permission; + @Nullable + private CommandExecutor executor; + @Nullable + private Map, CommandCallable> childCommandMap; + private boolean childCommandFallback = true; + private InputTokenizer argumentParser = InputTokenizer.quotedStrings(false); + + Builder() { + } + + /** + * Sets the permission that will be checked before using this command. + * + * @param permission The permission to check + * @return this + */ + public Builder permission(String permission) { + this.permission = permission; + return this; + } + + /** + * Sets the callback that will handle this command's execution. + * + * @param executor The executor that will be called with this command's + * parsed arguments + * @return this + */ + public Builder executor(CommandExecutor executor) { + Preconditions.checkNotNull(executor, "executor"); + this.executor = executor; + return this; + } + + /** + * Adds more child arguments for this command. + * If an executor or arguments are set, they are used as fallbacks. + * + * @param children The children to use + * @return this + */ + public Builder children(Map, ? extends CommandCallable> children) { + Preconditions.checkNotNull(children, "children"); + + if (this.childCommandMap == null) { + this.childCommandMap = new HashMap<>(); + } + + this.childCommandMap.putAll(children); + return this; + } + + /** + * Add a single child command to this command. + * + * @param child The child to add + * @param aliases Aliases to make the child available under. First + * one is primary and is the only one guaranteed to be listed in + * usage outputs. + * @return this + */ + public Builder child(CommandCallable child, String... aliases) { + if (this.childCommandMap == null) { + this.childCommandMap = new HashMap<>(); + } + + this.childCommandMap.put(ImmutableList.copyOf(aliases), child); + return this; + } + + /** + * Add a single child command to this command. + * + * @param child The child to add. + * @param aliases Aliases to make the child available under. First + * one is primary and is the only one guaranteed to be listed in + * usage outputs. + * @return this + */ + public Builder child(CommandCallable child, Collection aliases) { + if (this.childCommandMap == null) { + this.childCommandMap = new HashMap<>(); + } + + this.childCommandMap.put(ImmutableList.copyOf(aliases), child); + return this; + } + + /** + * A short, one-line description of this command's purpose. + * + * @param description The description to set + * @return this + */ + public Builder description(@Nullable ChatMessage description) { + this.description = description; + return this; + } + + /** + * Sets an extended description to use in longer help listings for this + * command. Will be appended to the short description and the command's + * usage. + * + * @param extendedDescription The description to set + * @return this + */ + public Builder extendedDescription(@Nullable ChatMessage extendedDescription) { + this.extendedDescription = extendedDescription; + return this; + } + + /** + * If a child command is selected but fails to parse arguments passed to + * it, the following determines the behavior. + * + *
    + *
  • If this is set to false, this command (the + * parent) will not attempt to parse the command, and will send back + * the error from the child.
  • + *
  • If this is set to true, the error from the + * child will simply be discarded, and the parent command will + * execute.
  • + *
+ * + *

The default for this is true, which emulates the + * behavior from previous API revisions.

+ * + * @param childCommandFallback Whether to fallback on argument parse + * failure + * @return this + */ + public Builder childArgumentParseExceptionFallback(boolean childCommandFallback) { + this.childCommandFallback = childCommandFallback; + return this; + } + + /** + * Sets the argument specification for this command. Generally, for a + * multi-argument command the {@link GenericArguments#seq(CommandElement...)} + * method is used to parse a sequence of args. + * + * @param args The arguments object to use + * @return this + * @see GenericArguments + */ + public Builder arguments(CommandElement args) { + Preconditions.checkNotNull(args, "args"); + this.args = GenericArguments.seq(args); + return this; + } + + /** + * Sets the argument specification for this command. This method accepts + * a sequence of arguments. This is equivalent to calling {@code + * arguments(seq(args))}. + * + * @param args The arguments object to use + * @return this + * @see GenericArguments + */ + public Builder arguments(CommandElement... args) { + Preconditions.checkNotNull(args, "args"); + this.args = GenericArguments.seq(args); + return this; + } + + /** + * Sets the input tokenizer to be used to convert input from a string + * into a list of argument tokens. + * + * @param parser The parser to use + * @return this + * @see InputTokenizer for common input parser implementations + */ + public Builder inputTokenizer(InputTokenizer parser) { + Preconditions.checkNotNull(parser, "parser"); + this.argumentParser = parser; + return this; + } + + /** + * Create a new {@link CommandSpec} based on the data provided in this + * builder. + * + * @return the new spec + */ + public CommandSpec build() { + if (this.childCommandMap == null || this.childCommandMap.isEmpty()) { + Preconditions.checkNotNull(this.executor, "An executor is required"); + } else if (this.executor == null) { + ChildCommandElementExecutor childCommandElementExecutor = + this.registerInDispatcher(new ChildCommandElementExecutor(null, null, false)); + if (this.args == DEFAULT_ARG) { + this.arguments(childCommandElementExecutor); + } else { + this.arguments(this.args, childCommandElementExecutor); + } + } else { + this.arguments(this.registerInDispatcher(new ChildCommandElementExecutor(this.executor, this.args, this.childCommandFallback))); + } + + return new CommandSpec(this.args, this.executor, this.description, this.extendedDescription, this.permission, + this.argumentParser); + } + + @SuppressWarnings({"ConstantConditions"}) + private ChildCommandElementExecutor registerInDispatcher(ChildCommandElementExecutor childDispatcher) { + for (Map.Entry, ? extends CommandCallable> spec : this.childCommandMap.entrySet()) { + childDispatcher.register(spec.getValue(), spec.getKey()); + } + + this.executor(childDispatcher); + return childDispatcher; + } + } + + /** + * Check the relevant permission for this command with the provided source, + * throwing an exception if the source does not have permission to use + * the command. + * + * @param source The source to check + * @throws CommandException if the source does not have permission + */ + public void checkPermission(PermissibleCommandSource source) throws CommandException { + Preconditions.checkNotNull(source, "source"); + + if (!this.testPermission(source)) { + throw new CommandPermissionException(); + } + } + + /** + * Process this command with existing arguments and context objects. + * + * @param source The source to populate the context with + * @param args The arguments to process with + * @param context The context to put data in + * @throws ArgumentParseException if an invalid argument is provided + */ + public void populateContext(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + this.args.parse(source, args, context); + + if (args.hasNext()) { + args.next(); + throw args.createError(ChatMessage.createTextMessage("Too many arguments!")); + } + } + + /** + * Return tab completion results using the existing parsed arguments and + * context. Primarily useful when including a subcommand in an existing + * specification. + * + * @param source The source to parse arguments for + * @param args The arguments object + * @param context The context object + * @return possible completions, or an empty list if none + */ + public List complete(PermissibleCommandSource source, CommandArgs args, CommandContext context) { + Preconditions.checkNotNull(source, "source"); + List ret = this.args.complete(source, args, context); + return ret == null ? ImmutableList.of() : ImmutableList.copyOf(ret); + } + + /** + * Gets the active executor for this command. Generally not a good idea to + * call this directly, unless you are handling arg parsing specially + * + * @return The active executor for this command + */ + public CommandExecutor getExecutor() { + return this.executor; + } + + /** + * Gets the active input tokenizer used for this command. + * + * @return This command's input tokenizer + */ + public InputTokenizer getInputTokenizer() { + return this.argumentParser; + } + + @Override + public CommandResult process(PermissibleCommandSource source, String arguments) throws CommandException { + this.checkPermission(source); + final CommandArgs args = new CommandArgs(arguments, this.getInputTokenizer().tokenize(arguments, false)); + final CommandContext context = new CommandContext(); + this.populateContext(source, args, context); + return this.getExecutor().execute(source, context); + } + + @Override + public List getSuggestions(PermissibleCommandSource source, String arguments, @Nullable Location targetPos) throws CommandException { + CommandArgs args = new CommandArgs(arguments, this.getInputTokenizer().tokenize(arguments, true)); + CommandContext ctx = new CommandContext(); + + if (targetPos != null) { + ctx.putArg(CommandContext.TARGET_BLOCK_ARG, targetPos); + } + + ctx.putArg(CommandContext.TAB_COMPLETION, true); + return this.complete(source, args, ctx); + } + + @Override + public boolean testPermission(PermissibleCommandSource source) { + return source.hasPermission(this.permission); + } + + /** + * Gets a short, one-line description used with this command if any is + * present. + * + * @return the short description. + */ + @Override + public Optional getShortDescription(PermissibleCommandSource source) { + return this.description; + } + + /** + * Gets the extended description used with this command if any is present. + * + * @param source The source to get the description for + * @return the extended description. + */ + public Optional getExtendedDescription(PermissibleCommandSource source) { + return this.extendedDescription; + } + + /** + * Gets the usage for this command appropriate for the provided command + * source. + * + * @param source The source + * @return the usage for the source + */ + @Override + public ChatMessage getUsage(PermissibleCommandSource source) { + Preconditions.checkNotNull(source, "source"); + return this.args.getUsage(source); + } + + /** + * Return a longer description for this command. This description is + * composed of at least all present of the short description, the usage + * statement, and the extended description + * + * @param source The source to get the extended description for + * @return the extended description + */ + @Override + public Optional getHelp(PermissibleCommandSource source) { + Preconditions.checkNotNull(source, "source"); + StringBuilder builder = new StringBuilder(); + this.getShortDescription(source).ifPresent((a) -> builder.append(a).append("\n")); + builder.append(this.getUsage(source)); + this.getExtendedDescription(source).ifPresent((a) -> builder.append(a).append("\n")); + return Optional.of(ChatMessage.createTextMessage(builder.toString())); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + + if (o == null || this.getClass() != o.getClass()) { + return false; + } + + CommandSpec that = (CommandSpec) o; + return Objects.equal(this.args, that.args) + && Objects.equal(this.executor, that.executor) + && Objects.equal(this.description, that.description) + && Objects.equal(this.extendedDescription, that.extendedDescription) + && Objects.equal(this.permission, that.permission) + && Objects.equal(this.argumentParser, that.argumentParser); + } + + @Override + public int hashCode() { + return Objects.hashCode(this.args, this.executor, this.description, this.extendedDescription, this.permission, this.argumentParser); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("args", this.args) + .add("executor", this.executor) + .add("description", this.description) + .add("extendedDescription", this.extendedDescription) + .add("permission", this.permission) + .add("argumentParser", this.argumentParser) + .toString(); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/CommandManagerImpl.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/CommandManagerImpl.java new file mode 100644 index 000000000..74edfb977 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/CommandManagerImpl.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.impl.command; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; + +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; +import net.minecraft.world.World; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandCallable; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandManager; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMapping; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMessageFormatting; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandPermissionException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandResult; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.InvocationCommandException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.dispatcher.Disambiguator; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.dispatcher.SimpleDispatcher; +import net.legacyfabric.fabric.api.logger.v1.Logger; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; +import net.legacyfabric.fabric.api.util.Location; +import net.legacyfabric.fabric.impl.logger.LoggerImpl; + +public class CommandManagerImpl implements CommandManager { + private static final Logger LOGGER = Logger.get(LoggerImpl.API, "Command Manager"); + private final Object lock = new Object(); + private final SimpleDispatcher dispatcher; + private final List mappings = Lists.newArrayList(); + + public CommandManagerImpl(Disambiguator disambiguator) { + this.dispatcher = new SimpleDispatcher(disambiguator); + } + + @Override + public Optional register(CommandCallable callable, String... alias) { + return this.register(callable, Arrays.asList(alias)); + } + + @Override + public Optional register(CommandCallable callable, List aliases) { + return this.register(callable, aliases, Function.identity()); + } + + @Override + public Optional register(CommandCallable callable, List aliases, Function, List> callback) { + synchronized (this.lock) { + return this.dispatcher.register(callable, aliases, callback).map(mapping -> { + this.mappings.add(mapping); + return mapping; + }); + } + } + + @Override + public Optional removeMapping(CommandMapping mapping) { + synchronized (this.lock) { + return this.dispatcher.removeMapping(mapping).map(commandMapping -> { + this.mappings.remove(commandMapping); + return commandMapping; + }); + } + } + + @Override + public int size() { + synchronized (this.lock) { + return this.dispatcher.size(); + } + } + + @Override + public CommandResult process(PermissibleCommandSource source, String command) { + final String[] argSplit = command.split(" ", 2); + + try { + try { + this.dispatcher.process(source, command); + } catch (InvocationCommandException e) { + if (e.getCause() != null) { + throw e.getCause(); + } + } catch (CommandPermissionException e) { + if (e.getText() != null) { + source.method_5505(CommandMessageFormatting.error(e.getText())); + } + } catch (CommandException e) { + ChatMessage text = e.getText(); + + if (text != null) { + source.method_5505(CommandMessageFormatting.error(text)); + } + + if (e.shouldIncludeUsage()) { + Optional mapping = this.dispatcher.get(argSplit[0], source); + + if (mapping.isPresent()) { + ChatMessage usage; + + if (e instanceof ArgumentParseException.WithUsage) { + usage = ((ArgumentParseException.WithUsage) e).getUsage(); + } else { + usage = mapping.get().getCallable().getUsage(source); + } + + source.method_5505(CommandMessageFormatting.error(ChatMessage.createTextMessage(String.format("Usage: /%s %s", argSplit[0], usage.toString())))); + } + } + } + } catch (Throwable t) { + LOGGER.error("An unexpected error happened executing a command"); + t.printStackTrace(); + + if (t instanceof Error) { + throw (Error) t; + } + + ChatMessage message = CommandMessageFormatting.error(ChatMessage.createTextMessage("An unexpected error happened executing the command")); + /* + Hover event doesn't exist in this minecraft version + message.setStyle(message.getStyle().setHoverEvent(new HoverEvent(HoverEventAction.SHOW_TEXT, ChatMessage.createTextMessage("Stacktrace: \n" + Arrays.stream(t.getStackTrace()).map(StackTraceElement::toString).collect(Collectors.joining("\n")))))); + */ + source.method_5505(message); + } + + return CommandResult.empty(); + } + + @Override + public List getSuggestions(PermissibleCommandSource source, String arguments, @Nullable Location targetPosition) { + try { + final String[] argSplit = arguments.split(" ", 2); + return Lists.newArrayList(this.dispatcher.getSuggestions(source, arguments, targetPosition)); + } catch (CommandException e) { + source.method_5505(CommandMessageFormatting.error(ChatMessage.createTextMessage(String.format("Error getting suggestions: %s", e.getText().toString())))); + return Collections.emptyList(); + } catch (Exception e) { + throw new RuntimeException(String.format("Error occured while tab completing '%s'", arguments), e); + } + } + + @Override + public boolean testPermission(PermissibleCommandSource source) { + synchronized (this.lock) { + return this.dispatcher.testPermission(source); + } + } + + @Override + public Optional getShortDescription(PermissibleCommandSource source) { + synchronized (this.lock) { + return this.dispatcher.getShortDescription(source); + } + } + + @Override + public Optional getHelp(PermissibleCommandSource source) { + synchronized (this.lock) { + return this.dispatcher.getHelp(source); + } + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource source) { + synchronized (this.lock) { + return this.dispatcher.getUsage(source); + } + } + + @Override + public Set getCommands() { + synchronized (this.lock) { + return this.dispatcher.getCommands(); + } + } + + @Override + public Set getPrimaryAliases() { + synchronized (this.lock) { + return this.dispatcher.getPrimaryAliases(); + } + } + + @Override + public Set getAliases() { + synchronized (this.lock) { + return this.dispatcher.getAliases(); + } + } + + @Override + public Optional get(String alias) { + synchronized (this.lock) { + return this.dispatcher.get(alias); + } + } + + @Override + public Optional get(String alias, @Nullable PermissibleCommandSource source) { + synchronized (this.lock) { + return this.dispatcher.get(alias, source); + } + } + + @Override + public Set getAll(String alias) { + synchronized (this.lock) { + return this.dispatcher.getAll(alias); + } + } + + protected List getMappings() { + return this.mappings; + } + + @Override + public Multimap getAll() { + synchronized (this.lock) { + return this.dispatcher.getAll(); + } + } + + @Override + public boolean containsAlias(String alias) { + synchronized (this.lock) { + return this.dispatcher.containsAlias(alias); + } + } + + @Override + public boolean containsMapping(CommandMapping mapping) { + synchronized (this.lock) { + return this.dispatcher.containsMapping(mapping); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/CommandWrapper.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/CommandWrapper.java new file mode 100644 index 000000000..09633fe07 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/CommandWrapper.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.impl.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.NotNull; + +import net.minecraft.command.AbstractCommand; +import net.minecraft.command.CommandSource; +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMapping; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMessageFormatting; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandPermissionException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.InvocationCommandException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; +import net.legacyfabric.fabric.api.util.Location; + +public class CommandWrapper extends AbstractCommand { + private final CommandMapping mapping; + + public CommandWrapper(CommandMapping mapping) { + this.mapping = mapping; + } + + @Override + public String getCommandName() { + return this.mapping.getPrimaryAlias(); + } + + @Override + public List getAliases() { + return new ArrayList<>(this.mapping.getAllAliases()); + } + + @Override + public String getUsageTranslationKey(CommandSource source) { + return this.mapping.getCallable().getHelp((PermissibleCommandSource) source).map(message -> message.toString(true)).orElse(""); + } + + @Override + public void execute(CommandSource source, String[] args) { + try { + try { + this.mapping.getCallable().process((PermissibleCommandSource) source, String.join(" ", args)); + } catch (InvocationCommandException e) { + if (e.getCause() != null) { + throw e.getCause(); + } + } catch (CommandPermissionException e) { + if (e.getText() != null) { + source.method_5505(CommandMessageFormatting.error(e.getText())); + } + } catch (CommandException e) { + ChatMessage text = e.getText(); + + if (text != null) { + source.method_5505(CommandMessageFormatting.error(text)); + } + + if (e.shouldIncludeUsage()) { + ChatMessage usage; + + if (e instanceof ArgumentParseException.WithUsage) { + usage = ((ArgumentParseException.WithUsage) e).getUsage(); + } else { + usage = this.mapping.getCallable().getUsage((PermissibleCommandSource) source); + } + + source.method_5505(CommandMessageFormatting.error(ChatMessage.createTextMessage(String.format("Usage: /%s %s", this.getCommandName(), usage.toString())))); + } + } + } catch (Throwable t) { + // Minecraft handles these exceptions for us + throw new RuntimeException(t); + } + } + + @Override + public boolean isAccessible(CommandSource source) { + return this.mapping.getCallable().testPermission((PermissibleCommandSource) source); + } + + @Override + public List method_3276(CommandSource source, String[] args) { + try { + return this.mapping.getCallable().getSuggestions((PermissibleCommandSource) source, Arrays.stream(args).collect(Collectors.joining(" ")), new Location<>(source.getWorld(), source.method_4086())); + } catch (CommandException e) { + source.method_5505(CommandMessageFormatting.error(ChatMessage.createTextMessage(String.format("Error getting suggestions: %s", e.getText().toString())))); + return Collections.emptyList(); + } catch (Exception e) { + throw new RuntimeException(String.format("Error occurred while providing auto complete hints for '%s'", String.join(" ", args)), e); + } + } + + //TODO: Autogenerated stub, make this actually work. + @Override + public int compareTo(@NotNull Object o) { + return 0; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/GuavaUtils.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/GuavaUtils.java new file mode 100644 index 000000000..da5fd2b6a --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/GuavaUtils.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.impl.command; + +import java.util.Arrays; + +import com.google.common.base.Preconditions; +import org.jetbrains.annotations.Nullable; + +/** + * Guava's ToStringHelper class which isn't located at the same place depending on guava versions. + * This ensures it is available in all targeted versions. + */ +public class GuavaUtils { + public static ToStringHelper toStringHelper(Object self) { + return new ToStringHelper(self.getClass().getSimpleName()); + } + + public static ToStringHelper toStringHelper(Class clazz) { + return new ToStringHelper(clazz.getSimpleName()); + } + + public static ToStringHelper toStringHelper(String className) { + return new ToStringHelper(className); + } + + public static final class ToStringHelper { + private final String className; + private final ValueHolder holderHead; + private ValueHolder holderTail; + private boolean omitNullValues; + + private ToStringHelper(String className) { + this.holderHead = new ValueHolder(); + this.holderTail = this.holderHead; + this.omitNullValues = false; + this.className = (String) Preconditions.checkNotNull(className); + } + + public ToStringHelper omitNullValues() { + this.omitNullValues = true; + return this; + } + + public ToStringHelper add(String name, @Nullable Object value) { + return this.addHolder(name, value); + } + + public ToStringHelper add(String name, boolean value) { + return this.addHolder(name, String.valueOf(value)); + } + + public ToStringHelper add(String name, char value) { + return this.addHolder(name, String.valueOf(value)); + } + + public ToStringHelper add(String name, double value) { + return this.addHolder(name, String.valueOf(value)); + } + + public ToStringHelper add(String name, float value) { + return this.addHolder(name, String.valueOf(value)); + } + + public ToStringHelper add(String name, int value) { + return this.addHolder(name, String.valueOf(value)); + } + + public ToStringHelper add(String name, long value) { + return this.addHolder(name, String.valueOf(value)); + } + + public ToStringHelper addValue(@Nullable Object value) { + return this.addHolder(value); + } + + public ToStringHelper addValue(boolean value) { + return this.addHolder(String.valueOf(value)); + } + + public ToStringHelper addValue(char value) { + return this.addHolder(String.valueOf(value)); + } + + public ToStringHelper addValue(double value) { + return this.addHolder(String.valueOf(value)); + } + + public ToStringHelper addValue(float value) { + return this.addHolder(String.valueOf(value)); + } + + public ToStringHelper addValue(int value) { + return this.addHolder(String.valueOf(value)); + } + + public ToStringHelper addValue(long value) { + return this.addHolder(String.valueOf(value)); + } + + public String toString() { + boolean omitNullValuesSnapshot = this.omitNullValues; + String nextSeparator = ""; + StringBuilder builder = (new StringBuilder(32)).append(this.className).append('{'); + + for (ValueHolder valueHolder = this.holderHead.next; valueHolder != null; valueHolder = valueHolder.next) { + Object value = valueHolder.value; + + if (!omitNullValuesSnapshot || value != null) { + builder.append(nextSeparator); + nextSeparator = ", "; + + if (valueHolder.name != null) { + builder.append(valueHolder.name).append('='); + } + + if (value != null && value.getClass().isArray()) { + Object[] objectArray = new Object[]{value}; + String arrayString = Arrays.deepToString(objectArray); + builder.append(arrayString, 1, arrayString.length() - 1); + } else { + builder.append(value); + } + } + } + + return builder.append('}').toString(); + } + + private ValueHolder addHolder() { + ValueHolder valueHolder = new ValueHolder(); + this.holderTail = this.holderTail.next = valueHolder; + return valueHolder; + } + + private ToStringHelper addHolder(@Nullable Object value) { + ValueHolder valueHolder = this.addHolder(); + valueHolder.value = value; + return this; + } + + private ToStringHelper addHolder(String name, @Nullable Object value) { + ValueHolder valueHolder = this.addHolder(); + valueHolder.value = value; + valueHolder.name = (String) Preconditions.checkNotNull(name); + return this; + } + + private static final class ValueHolder { + String name; + Object value; + ValueHolder next; + + private ValueHolder() { + } + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/ImplInit.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/ImplInit.java new file mode 100644 index 000000000..8858416fe --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/ImplInit.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.impl.command; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.api.DedicatedServerModInitializer; + +import net.legacyfabric.fabric.api.command.v2.CommandRegistrar; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandManager; +import net.legacyfabric.fabric.api.registry.CommandRegistry; + +public class ImplInit implements DedicatedServerModInitializer, ClientModInitializer, CommandRegistrar { + @Override + public void register(CommandManager manager, boolean dedicated) { + CommandRegistrar.EVENT.invoker().register(manager, dedicated); + InternalObjects.getCommandManager().getCommands().forEach(mapping -> { + CommandWrapper wrapper = new CommandWrapper(mapping); + CommandRegistry.INSTANCE.register(wrapper); + }); + } + + @Override + public void onInitializeClient() { + this.register(InternalObjects.getCommandManager(), false); + } + + @Override + public void onInitializeServer() { + this.register(InternalObjects.getCommandManager(), true); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/InternalObjects.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/InternalObjects.java new file mode 100644 index 000000000..aa590d8c4 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/InternalObjects.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.impl.command; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.dispatcher.SimpleDispatcher; + +public class InternalObjects { + private static final CommandManagerImpl COMMAND_MANAGER = new CommandManagerImpl(SimpleDispatcher.FIRST_DISAMBIGUATOR); + + public static CommandManagerImpl getCommandManager() { + return COMMAND_MANAGER; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/AllOfCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/AllOfCommandElement.java new file mode 100644 index 000000000..419b3b853 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/AllOfCommandElement.java @@ -0,0 +1,91 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMessageFormatting; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class AllOfCommandElement extends CommandElement { + private final CommandElement element; + + public AllOfCommandElement(CommandElement element) { + super(null); + this.element = element; + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + while (args.hasNext()) { + this.element.parse(source, args, context); + } + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return null; + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + CommandArgs.Snapshot startState = null; + + try { + while (args.hasNext()) { + startState = args.getSnapshot(); + this.element.parse(src, args, context); + } + } catch (ArgumentParseException e) { + // ignored + } + + // The final element, if an exception was not thrown, might have more completions available to it. + // Therefore, we reapply the last snapshot and complete from it. + if (startState != null) { + args.applySnapshot(startState); + } + + if (args.canComplete()) { + return this.element.complete(src, args, context); + } + + // If we have more elements, do not complete. + return Collections.emptyList(); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource context) { + return ChatMessage.createTextMessage(this.element.getUsage(context).toString() + CommandMessageFormatting.STAR_TEXT.toString(true)); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/BigDecimalElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/BigDecimalElement.java new file mode 100644 index 000000000..a0fa95086 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/BigDecimalElement.java @@ -0,0 +1,55 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.math.BigDecimal; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class BigDecimalElement extends KeyElement { + public BigDecimalElement(ChatMessage key) { + super(key); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + String next = args.next(); + + try { + return new BigDecimal(next); + } catch (NumberFormatException ex) { + throw args.createError(ChatMessage.createTextMessage("Expected a number, but input " + next + " was not")); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/BigIntegerElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/BigIntegerElement.java new file mode 100644 index 000000000..241192610 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/BigIntegerElement.java @@ -0,0 +1,55 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.math.BigInteger; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class BigIntegerElement extends KeyElement { + public BigIntegerElement(ChatMessage key) { + super(key); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + String integerString = args.next(); + + try { + return new BigInteger(integerString); + } catch (NumberFormatException ex) { + throw args.createError(ChatMessage.createTextMessage("Expected an integer, but input " + integerString + " was not")); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/ChoicesCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/ChoicesCommandElement.java new file mode 100644 index 000000000..d78f2fa2c --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/ChoicesCommandElement.java @@ -0,0 +1,98 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMessageFormatting; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; +import net.legacyfabric.fabric.api.util.TriState; + +public class ChoicesCommandElement extends CommandElement { + public static final int CUTOFF = 5; + private final Supplier> keySupplier; + private final Function valueSupplier; + private final TriState choicesInUsage; + + public ChoicesCommandElement(ChatMessage key, Supplier> keySupplier, Function valueSupplier, TriState choicesInUsage) { + super(key); + this.keySupplier = keySupplier; + this.valueSupplier = valueSupplier; + this.choicesInUsage = choicesInUsage; + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + Object value = this.valueSupplier.apply(args.next()); + + if (value == null) { + throw args.createError(ChatMessage.createTextMessage(String.format("Argument was not a valid choice. Valid choices: %s", this.keySupplier.get().toString()))); + } + + return value; + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + final String prefix = args.nextIfPresent().orElse(""); + return Collections.unmodifiableList(this.keySupplier.get().stream().filter((input) -> input.startsWith(prefix)).collect(Collectors.toList())); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource commander) { + Collection keys = this.keySupplier.get(); + + if (this.choicesInUsage == TriState.TRUE || (this.choicesInUsage == TriState.DEFAULT && keys.size() <= CUTOFF)) { + final ChatMessage build = ChatMessage.createTextMessage(""); + build.addUsing(CommandMessageFormatting.LT_TEXT); + + for (Iterator it = keys.iterator(); it.hasNext(); ) { + build.addUsing(ChatMessage.createTextMessage(it.next())); + + if (it.hasNext()) { + build.addUsing(CommandMessageFormatting.PIPE_TEXT); + } + } + + build.addUsing(CommandMessageFormatting.GT_TEXT); + return ChatMessage.createTextMessage(build.toString()); + } + + return super.getUsage(commander); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/DateTimeElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/DateTimeElement.java new file mode 100644 index 000000000..ac852a76a --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/DateTimeElement.java @@ -0,0 +1,102 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeParseException; +import java.util.List; + +import com.google.common.collect.ImmutableList; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class DateTimeElement extends CommandElement { + private final boolean returnNow; + + public DateTimeElement(ChatMessage key, boolean returnNow) { + super(key); + this.returnNow = returnNow; + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + if (!args.hasNext() && this.returnNow) { + return LocalDateTime.now(); + } + + CommandArgs.Snapshot state = args.getSnapshot(); + String date = args.next(); + + try { + return LocalDateTime.parse(date); + } catch (DateTimeParseException ex) { + try { + return LocalDateTime.of(LocalDate.now(), LocalTime.parse(date)); + } catch (DateTimeParseException ex2) { + try { + return LocalDateTime.of(LocalDate.parse(date), LocalTime.MIDNIGHT); + } catch (DateTimeParseException ex3) { + if (this.returnNow) { + args.applySnapshot(state); + return LocalDateTime.now(); + } + + throw args.createError(ChatMessage.createTextMessage("Invalid date-time!")); + } + } + } + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + String date = LocalDateTime.now().withNano(0).toString(); + + if (date.startsWith(args.nextIfPresent().orElse(""))) { + return ImmutableList.of(date); + } else { + return ImmutableList.of(); + } + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + if (!this.returnNow) { + return super.getUsage(src); + } else { + return ChatMessage.createTextMessage("[" + this.getKey().toString() + "]"); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/DurationElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/DurationElement.java new file mode 100644 index 000000000..2a1b9a64a --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/DurationElement.java @@ -0,0 +1,74 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.time.Duration; +import java.time.format.DateTimeParseException; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class DurationElement extends KeyElement { + public DurationElement(ChatMessage key) { + super(key); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + String s = args.next().toUpperCase(); + + if (!s.contains("T")) { + if (s.contains("D")) { + if (s.contains("H") || s.contains("M") || s.contains("S")) { + s = s.replace("D", "DT"); + } + } else { + if (s.startsWith("P")) { + s = "PT" + s.substring(1); + } else { + s = "T" + s; + } + } + } + + if (!s.startsWith("P")) { + s = "P" + s; + } + + try { + return Duration.parse(s); + } catch (DateTimeParseException ex) { + throw args.createError(ChatMessage.createTextMessage("Invalid duration!")); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/EntityCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/EntityCommandElement.java new file mode 100644 index 000000000..c24426fa5 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/EntityCommandElement.java @@ -0,0 +1,172 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import com.google.common.collect.Sets; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.SelectorCommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class EntityCommandElement extends SelectorCommandElement { + private final boolean returnTarget; + private final boolean returnSource; + @Nullable + private final Class clazz; + + public EntityCommandElement(ChatMessage key, boolean returnSource, boolean returnTarget, @Nullable Class clazz) { + super(key); + this.returnSource = returnSource; + this.returnTarget = returnTarget; + this.clazz = clazz; + } + + @SuppressWarnings("unchecked") + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + if (!args.hasNext()) { + if (this.returnSource) { + return this.tryReturnSource(source, args, true); + } + + if (this.returnTarget) { + return this.tryReturnTarget(source, args); + } + } + + CommandArgs.Snapshot state = args.getSnapshot(); + + try { + Iterable entities = (Iterable) super.parseValue(source, args); + + for (Entity entity : entities) { + if (!this.checkEntity(entity)) { + ChatMessage name = ChatMessage.createTextMessage(this.clazz == null ? "null" : this.clazz.getSimpleName()); + throw args.createError(ChatMessage.createTextMessage("The entity is not of the required type! (").addUsing(name).addText(")")); + } + } + + return entities; + } catch (ArgumentParseException ex) { + if (this.returnSource) { + args.applySnapshot(state); + return this.tryReturnSource(source, args, true); + } + + throw ex; + } + } + + @Override + protected Iterable getChoices(PermissibleCommandSource source) { + Set worldEntities = (Set) Arrays.stream(MinecraftServer.getServer().worlds).flatMap(world -> world.entities.stream()) + .filter(o -> this.checkEntity((Entity) o)) + .map(entity -> ((Entity) entity).getUuid().toString()).collect(Collectors.toSet()); + Collection players = Sets.newHashSet(MinecraftServer.getServer().getPlayerManager().players); + + if (!players.isEmpty() && this.checkEntity(players.iterator().next())) { + final Set setToReturn = Sets.newHashSet(worldEntities); // to ensure mutability + players.forEach(x -> setToReturn.add(x.getTranslationKey())); + return setToReturn; + } + + return worldEntities; + } + + @Override + protected Object getValue(String choice) throws IllegalArgumentException { + UUID uuid; + + try { + uuid = UUID.fromString(choice); + } catch (IllegalArgumentException ignored) { + // Player could be a name + return Optional.ofNullable(this.getServer().getPlayerManager().getPlayer(choice)).orElseThrow(() -> new IllegalArgumentException("Input value " + choice + " does not represent a valid entity")); + } + + boolean found = false; + Optional ret = Optional.ofNullable((Entity) this.getServer().getWorld().entities.stream() + .filter(o -> ((Entity) o).getUuid().equals(uuid)).findFirst().orElse(null)); + + if (ret.isPresent()) { + Entity entity = ret.get(); + + if (this.checkEntity(entity)) { + return entity; + } + + found = true; + } + + if (found) { + throw new IllegalArgumentException("Input value " + choice + " was not an entity of the required type!"); + } + + throw new IllegalArgumentException("Input value " + choice + " was not an entity"); + } + + private Entity tryReturnSource(PermissibleCommandSource source, CommandArgs args, boolean check) throws ArgumentParseException { + if (source instanceof Entity && (!check || this.checkEntity((Entity) source))) { + return (Entity) source; + } + + throw args.createError(ChatMessage.createTextMessage("No entities matched and source was not an entity!")); + } + + // TODO + private Entity tryReturnTarget(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + Entity entity = this.tryReturnSource(source, args, false); + throw args.createError(ChatMessage.createTextMessage("No entities matched and source was not looking at a valid entity!")); + // return entity.getWorld().getIntersectingEntities(entity, 10).stream().filter(e -> !e.getEntity().equals(entity)).map(EntityUniverse.EntityHit::getEntity).filter(this::checkEntity).findFirst().orElseThrow(() -> args.createError(new LiteralText("No entities matched and source was not looking at a valid entity!"))); + } + + private boolean checkEntity(Entity entity) { + if (this.clazz == null) { + return true; + } else { + return this.clazz.isAssignableFrom(entity.getClass()); + } + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + return src instanceof Entity && (this.returnSource || this.returnTarget) ? ChatMessage.createTextMessage("[" + this.getKey().toString() + "]") : super.getUsage(src); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/EnumValueElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/EnumValueElement.java new file mode 100644 index 000000000..507a5e50f --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/EnumValueElement.java @@ -0,0 +1,68 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.PatternMatchingCommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class EnumValueElement> extends PatternMatchingCommandElement { + private final Class type; + private final Map values; + + public EnumValueElement(ChatMessage key, Class type) { + super(key); + this.type = type; + this.values = Arrays.stream(type.getEnumConstants()) + .collect(Collectors.toMap(value -> value.name().toLowerCase(), + Function.identity(), (value, value2) -> { + throw new UnsupportedOperationException(type.getCanonicalName() + " contains more than one enum constant " + "with the same name, only differing by capitalization, which is unsupported."); + } + )); + } + + @Override + protected Iterable getChoices(PermissibleCommandSource source) { + return this.values.keySet(); + } + + @Override + protected Object getValue(String choice) throws IllegalArgumentException { + T value = this.values.get(choice.toLowerCase()); + + if (value == null) { + throw new IllegalArgumentException("No enum constant " + this.type.getCanonicalName() + "." + choice); + } + + return value; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/FilteredSuggestionsElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/FilteredSuggestionsElement.java new file mode 100644 index 000000000..01d7c3883 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/FilteredSuggestionsElement.java @@ -0,0 +1,61 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.Nullable; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class FilteredSuggestionsElement extends CommandElement { + private final CommandElement wrapped; + private final Predicate predicate; + + public FilteredSuggestionsElement(CommandElement wrapped, Predicate predicate) { + super(wrapped.getKey()); + this.wrapped = wrapped; + this.predicate = predicate; + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return this.wrapped.parseValue(source, args); + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + return Collections.unmodifiableList(this.wrapped.complete(src, args, context).stream().filter(this.predicate).collect(Collectors.toList())); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/FirstParsingCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/FirstParsingCommandElement.java new file mode 100644 index 000000000..8a8942400 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/FirstParsingCommandElement.java @@ -0,0 +1,109 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMessageFormatting; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class FirstParsingCommandElement extends CommandElement { + private final List elements; + + public FirstParsingCommandElement(List elements) { + super(null); + this.elements = elements; + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + ArgumentParseException lastException = null; + + for (CommandElement element : this.elements) { + CommandArgs.Snapshot startState = args.getSnapshot(); + CommandContext.Snapshot contextSnapshot = context.createSnapshot(); + + try { + element.parse(source, args, context); + return; + } catch (ArgumentParseException ex) { + lastException = ex; + args.applySnapshot(startState); + context.applySnapshot(contextSnapshot); + } + } + + if (lastException != null) { + throw lastException; + } + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return null; + } + + @Override + public List complete(final PermissibleCommandSource src, final CommandArgs args, final CommandContext context) { + return Lists.newLinkedList(Iterables.concat(this.elements.stream().map(element -> { + if (element == null) { + return ImmutableList.of(); + } else { + CommandArgs.Snapshot snapshot = args.getSnapshot(); + List ret = element.complete(src, args, context); + args.applySnapshot(snapshot); + return ret; + } + }).collect(Collectors.toSet()))); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource commander) { + final ChatMessage ret = ChatMessage.createTextMessage(""); + + for (Iterator it = this.elements.iterator(); it.hasNext(); ) { + ret.addUsing(it.next().getUsage(commander)); + + if (it.hasNext()) { + ret.addUsing(CommandMessageFormatting.PIPE_TEXT); + } + } + + return ChatMessage.createTextMessage(ret.toString()); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/IpElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/IpElement.java new file mode 100644 index 000000000..cfa202967 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/IpElement.java @@ -0,0 +1,65 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class IpElement extends KeyElement { + private final PlayerCommandElement possiblePlayer; + + public IpElement(ChatMessage key) { + super(key); + this.possiblePlayer = new PlayerCommandElement(key, false); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + String s = args.next(); + + try { + return InetAddress.getByName(s); + } catch (UnknownHostException e) { + try { + return ((ServerPlayerEntity) Objects.requireNonNull(this.possiblePlayer.parseValue(source, args))).field_2823.connection.getAddress(); + } catch (ArgumentParseException ex) { + throw args.createError(ChatMessage.createTextMessage("Invalid IP address!")); + } + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/LiteralCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/LiteralCommandElement.java new file mode 100644 index 000000000..02f42e43c --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/LiteralCommandElement.java @@ -0,0 +1,93 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.List; +import java.util.Optional; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class LiteralCommandElement extends CommandElement { + private final List expectedArgs; + @Nullable + private final Object putValue; + + public LiteralCommandElement(@Nullable ChatMessage key, List expectedArgs, @Nullable Object putValue) { + super(key); + this.expectedArgs = ImmutableList.copyOf(expectedArgs); + this.putValue = putValue; + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + for (String arg : this.expectedArgs) { + String current; + + if (!(current = args.next()).equalsIgnoreCase(arg)) { + throw args.createError(ChatMessage.createTextMessage(String.format("Argument %s did not match expected next argument %s", current, arg))); + } + } + + return this.putValue; + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + for (String arg : this.expectedArgs) { + final Optional next = args.nextIfPresent(); + + if (!next.isPresent()) { + break; + } else if (args.hasNext()) { + if (!next.get().equalsIgnoreCase(arg)) { + break; + } + } else { + if (arg.toLowerCase().startsWith(next.get().toLowerCase())) { // Case-insensitive compare + return ImmutableList.of(arg); // TODO: Possibly complete all remaining args? Does that even work + } + } + } + + return ImmutableList.of(); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + return ChatMessage.createTextMessage(Joiner.on(' ').join(this.expectedArgs)); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/MarkTrueCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/MarkTrueCommandElement.java new file mode 100644 index 000000000..c463f2c98 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/MarkTrueCommandElement.java @@ -0,0 +1,58 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class MarkTrueCommandElement extends CommandElement { + public MarkTrueCommandElement(ChatMessage key) { + super(key); + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return true; + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + return Collections.emptyList(); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + return ChatMessage.createTextMessage(""); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/ModCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/ModCommandElement.java new file mode 100644 index 000000000..0bfd7542f --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/ModCommandElement.java @@ -0,0 +1,56 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Optional; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.PatternMatchingCommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class ModCommandElement extends PatternMatchingCommandElement { + public ModCommandElement(@Nullable ChatMessage key) { + super(key); + } + + @Override + protected Iterable getChoices(PermissibleCommandSource source) { + return FabricLoader.getInstance().getAllMods().stream().map(container -> container.getMetadata().getId()).collect(Collectors.toSet()); + } + + @Override + protected Object getValue(String choice) throws IllegalArgumentException { + Optional plugin = FabricLoader.getInstance().getModContainer(choice); + return plugin.orElseThrow(() -> new IllegalArgumentException("Mod " + choice + " was not found")); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/NumericElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/NumericElement.java new file mode 100644 index 000000000..9f1b83f10 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/NumericElement.java @@ -0,0 +1,71 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class NumericElement extends KeyElement { + private final Function parseFunc; + @Nullable + private final BiFunction parseRadixFunction; + private final Function errorSupplier; + + public NumericElement(ChatMessage key, Function parseFunc, @Nullable BiFunction parseRadixFunction, Function errorSupplier) { + super(key); + this.parseFunc = parseFunc; + this.parseRadixFunction = parseRadixFunction; + this.errorSupplier = errorSupplier; + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + final String input = args.next(); + + try { + if (this.parseRadixFunction != null) { + if (input.startsWith("0x")) { + return this.parseRadixFunction.apply(input.substring(2), 16); + } else if (input.startsWith("0b")) { + return this.parseRadixFunction.apply(input.substring(2), 2); + } + } + + return this.parseFunc.apply(input); + } catch (NumberFormatException ex) { + throw args.createError(this.errorSupplier.apply(input)); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/OnlyOneCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/OnlyOneCommandElement.java new file mode 100644 index 000000000..b614cf041 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/OnlyOneCommandElement.java @@ -0,0 +1,73 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class OnlyOneCommandElement extends CommandElement { + private final CommandElement element; + + public OnlyOneCommandElement(CommandElement element) { + super(element.getKey()); + this.element = element; + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + this.element.parse(source, args, context); + + if (context.getAll(this.element.getUntranslatedKey()).size() > 1) { + ChatMessage key = this.element.getKey(); + throw args.createError(ChatMessage.createTextMessage(String.format("Argument %s may have only one value!", key != null ? key.toString() : "unknown"))); + } + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + return this.element.getUsage(src); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return this.element.parseValue(source, args); + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + return this.element.complete(src, args, context); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/OptionalCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/OptionalCommandElement.java new file mode 100644 index 000000000..e8e847585 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/OptionalCommandElement.java @@ -0,0 +1,102 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class OptionalCommandElement extends CommandElement { + private final CommandElement element; + @Nullable + private final Object value; + private final boolean considerInvalidFormatEmpty; + + public OptionalCommandElement(CommandElement element, @Nullable Object value, boolean considerInvalidFormatEmpty) { + super(null); + this.element = element; + this.value = value; + this.considerInvalidFormatEmpty = considerInvalidFormatEmpty; + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + if (!args.hasNext()) { + ChatMessage key = this.element.getKey(); + + if (key != null && this.value != null) { + context.putArg(key.toString(true), this.value); + } + + return; + } + + CommandArgs.Snapshot startState = args.getSnapshot(); + + try { + this.element.parse(source, args, context); + } catch (ArgumentParseException ex) { + if (this.considerInvalidFormatEmpty || args.hasNext()) { // If there are more args, suppress. Otherwise, throw the error + args.applySnapshot(startState); + + if (this.element.getKey() != null && this.value != null) { + context.putArg(this.element.getUntranslatedKey(), this.value); + } + } else { + throw ex; + } + } + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return args.hasNext() ? null : this.element.parseValue(source, args); + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + return this.element.complete(src, args, context); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + final ChatMessage containingUsage = this.element.getUsage(src); + + if (containingUsage.toString().isEmpty()) { + return ChatMessage.createTextMessage(""); + } + + return ChatMessage.createTextMessage("[" + this.element.getUsage(src).toString() + "]"); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/PermissionCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/PermissionCommandElement.java new file mode 100644 index 000000000..fb7597752 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/PermissionCommandElement.java @@ -0,0 +1,100 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.List; + +import com.google.common.collect.ImmutableList; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class PermissionCommandElement extends CommandElement { + private final CommandElement element; + private final String permission; + private final boolean isOptional; + + public PermissionCommandElement(CommandElement element, String permission, boolean isOptional) { + super(element.getKey()); + this.element = element; + this.permission = permission; + this.isOptional = isOptional; + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + if (this.checkPermission(source, args)) { + return this.element.parseValue(source, args); + } + + return null; + } + + private boolean checkPermission(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + boolean hasPermission = source.hasPermission(this.permission); + + if (!hasPermission && !this.isOptional) { + ChatMessage key = this.getKey(); + throw args.createError(ChatMessage.createTextMessage(String.format("You do not have permission to use the %s argument", key != null ? key.toString() : "unknown"))); + } + + return hasPermission; + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + boolean flag = src.hasPermission(this.permission); + + if (!flag) { + return ImmutableList.of(); + } + + return this.element.complete(src, args, context); + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + if (this.checkPermission(source, args)) { + this.element.parse(source, args, context); + } + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + if (this.isOptional && !src.hasPermission(this.permission)) { + return ChatMessage.createTextMessage(""); + } + + return this.element.getUsage(src); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/PlayerCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/PlayerCommandElement.java new file mode 100644 index 000000000..9c479aeab --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/PlayerCommandElement.java @@ -0,0 +1,99 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.SelectorCommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class PlayerCommandElement extends SelectorCommandElement { + private final boolean returnSource; + + public PlayerCommandElement(ChatMessage key, boolean returnSource) { + super(key); + this.returnSource = returnSource; + } + + @SuppressWarnings("unchecked") + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + if (!args.hasNext() && this.returnSource) { + return this.tryReturnSource(source, args); + } + + CommandArgs.Snapshot state = args.getSnapshot(); + + try { + return StreamSupport.stream(((Iterable) super.parseValue(source, args)).spliterator(), false).filter(e -> e instanceof PlayerEntity).collect(Collectors.toList()); + } catch (ArgumentParseException ex) { + if (this.returnSource) { + args.applySnapshot(state); + return this.tryReturnSource(source, args); + } + + throw ex; + } + } + + @Override + protected Iterable getChoices(PermissibleCommandSource source) { + return (Iterable) MinecraftServer.getServer().getPlayerManager().players.stream().map(player -> ((PlayerEntity) player).getUsername()).collect(Collectors.toSet()); + } + + @Override + protected Object getValue(String choice) throws IllegalArgumentException { + Optional ret = this.getServer().getPlayerManager().players.stream().findFirst(); + + if (!ret.isPresent()) { + throw new IllegalArgumentException("Input value " + choice + " was not a player"); + } + + return ret.get(); + } + + private PlayerEntity tryReturnSource(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + if (source instanceof PlayerEntity) { + return ((PlayerEntity) source); + } else { + throw args.createError(ChatMessage.createTextMessage("No players matched and source was not a player!")); + } + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + return src != null && this.returnSource ? ChatMessage.createTextMessage("[" + super.getUsage(src).toString() + "]") : super.getUsage(src); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/RemainingJoinedStringsCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/RemainingJoinedStringsCommandElement.java new file mode 100644 index 000000000..27b2f06f9 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/RemainingJoinedStringsCommandElement.java @@ -0,0 +1,70 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMessageFormatting; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class RemainingJoinedStringsCommandElement extends KeyElement { + private final boolean raw; + + public RemainingJoinedStringsCommandElement(ChatMessage key, boolean raw) { + super(key); + this.raw = raw; + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + if (this.raw) { + args.next(); + String ret = args.getRaw().substring(args.getRawPosition()); + + while (args.hasNext()) { // Consume remaining args + args.next(); + } + + return ret; + } + + final StringBuilder ret = new StringBuilder(args.next()); + + while (args.hasNext()) { + ret.append(' ').append(args.next()); + } + + return ret.toString(); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + return ChatMessage.createTextMessage("").addText(CommandMessageFormatting.LT_TEXT.toString()).addUsing(this.getKey()).addUsing(CommandMessageFormatting.ELLIPSIS_TEXT).addUsing(CommandMessageFormatting.GT_TEXT); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/RepeatedCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/RepeatedCommandElement.java new file mode 100644 index 000000000..121917538 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/RepeatedCommandElement.java @@ -0,0 +1,81 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class RepeatedCommandElement extends CommandElement { + private final CommandElement element; + private final int times; + + public RepeatedCommandElement(CommandElement element, int times) { + super(null); + this.element = element; + this.times = times; + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + for (int i = 0; i < this.times; ++i) { + this.element.parse(source, args, context); + } + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return null; + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + for (int i = 0; i < this.times; ++i) { + CommandArgs.Snapshot startState = args.getSnapshot(); + + try { + this.element.parse(src, args, context); + } catch (ArgumentParseException e) { + args.applySnapshot(startState); + return this.element.complete(src, args, context); + } + } + + return Collections.emptyList(); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource src) { + return ChatMessage.createTextMessage(this.times + '*' + this.element.getUsage(src).toString()); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/SequenceCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/SequenceCommandElement.java new file mode 100644 index 000000000..35fb17ca1 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/SequenceCommandElement.java @@ -0,0 +1,133 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandMessageFormatting; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class SequenceCommandElement extends CommandElement { + private final List elements; + + public SequenceCommandElement(List elements) { + super(null); + this.elements = elements; + } + + @Override + public void parse(PermissibleCommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + for (CommandElement element : this.elements) { + element.parse(source, args, context); + } + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return null; + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + Set completions = Sets.newHashSet(); + + for (CommandElement element : this.elements) { + CommandArgs.Snapshot state = args.getSnapshot(); + CommandContext.Snapshot contextSnapshot = context.createSnapshot(); + + try { + element.parse(src, args, context); + + // If we get here, the parse occurred successfully. + // However, if nothing was consumed, then we should consider + // what could have been. + CommandContext.Snapshot afterSnapshot = context.createSnapshot(); + + if (state.equals(args.getSnapshot())) { + context.applySnapshot(contextSnapshot); + completions.addAll(element.complete(src, args, context)); + args.applySnapshot(state); + context.applySnapshot(afterSnapshot); + } else if (args.hasNext()) { + completions.clear(); + } else { + // What we might also have - we have no args left to parse so + // while the parse itself was successful, there could be other + // valid entries to add... + context.applySnapshot(contextSnapshot); + args.applySnapshot(state); + completions.addAll(element.complete(src, args, context)); + + if (!(element instanceof OptionalCommandElement)) { + break; + } + + // The last element was optional, so we go back to before this + // element would have been parsed, and assume it never existed... + context.applySnapshot(contextSnapshot); + args.applySnapshot(state); + } + } catch (ArgumentParseException ignored) { + args.applySnapshot(state); + context.applySnapshot(contextSnapshot); + completions.addAll(element.complete(src, args, context)); + break; + } + } + + return Lists.newArrayList(completions); + } + + @Override + public ChatMessage getUsage(PermissibleCommandSource commander) { + final ChatMessage build = ChatMessage.createTextMessage(""); + + for (Iterator it = this.elements.iterator(); it.hasNext(); ) { + ChatMessage usage = it.next().getUsage(commander); + + if (!usage.toString().isEmpty()) { + build.addUsing(usage); + + if (it.hasNext()) { + build.addUsing(CommandMessageFormatting.SPACE_TEXT); + } + } + } + + return ChatMessage.createTextMessage(build.toString()); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/StringCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/StringCommandElement.java new file mode 100644 index 000000000..de007ee91 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/StringCommandElement.java @@ -0,0 +1,53 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.StringType; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class StringCommandElement extends KeyElement { + private final StringType stringType; + private final RemainingJoinedStringsCommandElement joinedElement; + + public StringCommandElement(ChatMessage key, StringType type) { + super(key); + this.stringType = type; + this.joinedElement = type.isAll() ? new RemainingJoinedStringsCommandElement(key, false) : null; + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return this.stringType.isAll() ? (String) this.joinedElement.parseValue(source, args) : args.next(); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/StringElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/StringElement.java new file mode 100644 index 000000000..baea0f13e --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/StringElement.java @@ -0,0 +1,44 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class StringElement extends KeyElement { + public StringElement(ChatMessage key) { + super(key); + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return args.next(); + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/UrlElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/UrlElement.java new file mode 100644 index 000000000..6e9faa9ed --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/UrlElement.java @@ -0,0 +1,66 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class UrlElement extends KeyElement { + public UrlElement(ChatMessage key) { + super(key); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + String str = args.next(); + URL url; + + try { + url = new URL(str); + } catch (MalformedURLException ex) { + throw new ArgumentParseException(ChatMessage.createTextMessage("Invalid URL!"), ex, str, 0); + } + + try { + url.toURI(); + } catch (URISyntaxException ex) { + throw new ArgumentParseException(ChatMessage.createTextMessage("Invalid URL!"), ex, str, 0); + } + + return url; + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/UuidElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/UuidElement.java new file mode 100644 index 000000000..7b6f7f2fc --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/UuidElement.java @@ -0,0 +1,53 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.UUID; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.KeyElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class UuidElement extends KeyElement { + public UuidElement(ChatMessage key) { + super(key); + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + try { + return UUID.fromString(args.next()); + } catch (IllegalArgumentException ex) { + throw args.createError(ChatMessage.createTextMessage("Invalid UUID!")); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/Vec3dCommandElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/Vec3dCommandElement.java new file mode 100644 index 000000000..81566f085 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/Vec3dCommandElement.java @@ -0,0 +1,141 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.text.ChatMessage; +import net.minecraft.util.math.Vec3d; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +/** + * A {@link Vec3d} command element. + * + *

It has the following syntax:

+ * + *
 x,y,z
+ * x y z.
+ * + *

Each element can be relative to a location? so + * parseRelativeDouble() -- relative is ~(num)

+ */ +public class Vec3dCommandElement extends CommandElement { + private static final ImmutableSet SPECIAL_TOKENS = ImmutableSet.of("#target", "#me"); + + public Vec3dCommandElement(@Nullable ChatMessage key) { + super(key); + } + + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + String xStr; + String yStr; + String zStr; + xStr = args.next(); + + if (xStr.contains(",")) { + String[] split = xStr.split(","); + + if (split.length != 3) { + throw args.createError(ChatMessage.createTextMessage(String.format("Comma-separated location must have 3 elements, not %s", split.length))); + } + + xStr = split[0]; + yStr = split[1]; + zStr = split[2]; + } else if (xStr.equalsIgnoreCase("#me")) { + return source.method_4086(); + } else { + yStr = args.next(); + zStr = args.next(); + } + + double x = this.parseRelativeDouble(args, xStr, (double) source.method_4086().x); + double y = this.parseRelativeDouble(args, yStr, (double) source.method_4086().y); + double z = this.parseRelativeDouble(args, zStr, (double) source.method_4086().z); + return Vec3d.method_604(x, y, z); + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + Optional arg = args.nextIfPresent(); + + // Traverse through the possible arguments. We can't really complete arbitrary integers + if (arg.isPresent()) { + if (arg.get().startsWith("#")) { + Optional finalArg = arg; + return Collections.unmodifiableList(SPECIAL_TOKENS.stream().filter((input) -> input.startsWith(finalArg.get())).collect(Collectors.toList())); + } else if (arg.get().contains(",") || !args.hasNext()) { + return ImmutableList.of(arg.get()); + } else { + arg = args.nextIfPresent(); + + if (args.hasNext()) { + return ImmutableList.of(args.nextIfPresent().get()); + } + + return ImmutableList.of(arg.get()); + } + } + + return ImmutableList.of(); + } + + private double parseRelativeDouble(CommandArgs args, String arg, @Nullable Double relativeTo) throws ArgumentParseException { + boolean relative = arg.startsWith("~"); + + if (relative) { + if (relativeTo == null) { + throw args.createError(ChatMessage.createTextMessage("Relative position specified but source does not have a position")); + } + + arg = arg.substring(1); + + if (arg.isEmpty()) { + return relativeTo; + } + } + + try { + double ret = Double.parseDouble(arg); + return relative ? ret + relativeTo : ret; + } catch (NumberFormatException e) { + throw args.createError(ChatMessage.createTextMessage(String.format("Expected input %s to be a double, but was not", arg))); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/WithSuggestionsElement.java b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/WithSuggestionsElement.java new file mode 100644 index 000000000..389e75583 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/java/net/legacyfabric/fabric/impl/command/lib/sponge/args/WithSuggestionsElement.java @@ -0,0 +1,69 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) 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 net.legacyfabric.fabric.impl.command.lib.sponge.args; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import com.google.common.collect.ImmutableList; +import org.jetbrains.annotations.Nullable; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.ArgumentParseException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandArgs; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandElement; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class WithSuggestionsElement extends CommandElement { + private final CommandElement wrapped; + private final Function> suggestions; + private final boolean requireBegin; + + public WithSuggestionsElement(CommandElement wrapped, Function> suggestions, boolean requireBegin) { + super(wrapped.getKey()); + this.wrapped = wrapped; + this.suggestions = suggestions; + this.requireBegin = requireBegin; + } + + @Nullable + @Override + public Object parseValue(PermissibleCommandSource source, CommandArgs args) throws ArgumentParseException { + return this.wrapped.parseValue(source, args); + } + + @Override + public List complete(PermissibleCommandSource src, CommandArgs args, CommandContext context) { + if (this.requireBegin) { + String arg = args.nextIfPresent().orElse(""); + return ImmutableList.copyOf(StreamSupport.stream(this.suggestions.apply(src).spliterator(), false).filter(f -> f.startsWith(arg)).collect(Collectors.toList())); + } else { + return ImmutableList.copyOf(this.suggestions.apply(src)); + } + } +} diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/resources/assets/legacy-fabric-sponge-command-api-v2/icon.png b/legacy-fabric-command-api-v2/1.6.4/src/main/resources/assets/legacy-fabric-sponge-command-api-v2/icon.png new file mode 100644 index 000000000..2931efbf6 Binary files /dev/null and b/legacy-fabric-command-api-v2/1.6.4/src/main/resources/assets/legacy-fabric-sponge-command-api-v2/icon.png differ diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/resources/command-api-v2.accesswidener b/legacy-fabric-command-api-v2/1.6.4/src/main/resources/command-api-v2.accesswidener new file mode 100644 index 000000000..d9d644bc6 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/resources/command-api-v2.accesswidener @@ -0,0 +1,5 @@ +accessWidener v2 named + +accessible method net/minecraft/text/ChatMessage getText ()Ljava/lang/String; +accessible method net/minecraft/text/ChatMessage getTranslate ()Ljava/lang/String; +accessible method net/minecraft/text/ChatMessage getUsing ()Ljava/util/List; diff --git a/legacy-fabric-command-api-v2/1.6.4/src/main/resources/fabric.mod.json b/legacy-fabric-command-api-v2/1.6.4/src/main/resources/fabric.mod.json new file mode 100644 index 000000000..052f41fe8 --- /dev/null +++ b/legacy-fabric-command-api-v2/1.6.4/src/main/resources/fabric.mod.json @@ -0,0 +1,45 @@ +{ + "schemaVersion": 1, + "id": "legacy-fabric-command-api-v2", + "name": "Legacy Fabric Command API (v2)", + "version": "${version}", + "environment": "*", + "license": "Apache-2.0", + "icon": "assets/legacy-fabric-sponge-command-api-v2/icon.png", + "contact": { + "homepage": "https://legacyfabric.net", + "irc": "irc://irc.esper.net:6667/legacyfabric", + "issues": "https://github.com/Legacy-Fabric/fabric/issues", + "sources": "https://github.com/Legacy-Fabric/fabric" + }, + "authors": [ + "Legacy-Fabric" + ], + "depends": { + "fabricloader": ">=0.4.0", + "minecraft": "${minecraft_version}" + }, + "entrypoints": { + "client": [ + "net.legacyfabric.fabric.impl.command.ImplInit" + ], + "server": [ + "net.legacyfabric.fabric.impl.command.ImplInit" + ] + }, + "description": "Powerful Command API that uses SpongeAPI's command api", + "mixins": [], + "accessWidener": "command-api-v2.accesswidener", + "custom": { + "modmenu": { + "badges": [ "library" ], + "parent": { + "id": "legacy-fabric-api", + "name": "Legacy Fabric API", + "badges": [ "library" ], + "description": "Core API module providing key hooks and inter-compatibility features for Minecraft 1.7.10-1.12.2.", + "icon": "assets/legacy-fabric-sponge-command-api-v2/icon.png" + } + } + } +} diff --git a/legacyfabric-api/1.6.4/src/testmod/java/net/legacyfabric/fabric/test/command/ModMetadataCommand.java b/legacyfabric-api/1.6.4/src/testmod/java/net/legacyfabric/fabric/test/command/ModMetadataCommand.java new file mode 100644 index 000000000..b2945c5f1 --- /dev/null +++ b/legacyfabric-api/1.6.4/src/testmod/java/net/legacyfabric/fabric/test/command/ModMetadataCommand.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.test.command; + +import java.util.Objects; + +import net.minecraft.text.ChatMessage; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.fabricmc.loader.api.metadata.ContactInformation; + +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandException; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandManager; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.CommandResult; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.CommandContext; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.args.GenericArguments; +import net.legacyfabric.fabric.api.command.v2.lib.sponge.spec.CommandSpec; +import net.legacyfabric.fabric.api.permission.v1.PermissibleCommandSource; + +public class ModMetadataCommand { + public static void register(CommandManager manager) { + manager.register( + CommandSpec.builder() + .arguments(GenericArguments.mod(ChatMessage.createTextMessage("modid"))) + .executor(ModMetadataCommand::execute) + .build(), + "modmetadata" + ); + } + + private static final boolean useStyle = !Objects.equals(FabricLoader.getInstance().getModContainer("minecraft").get().getMetadata().getVersion().getFriendlyString(), "1.8"); + + private static CommandResult execute(PermissibleCommandSource source, CommandContext ctx) throws CommandException { + ModContainer container = ctx.getOne("modid").orElseThrow(() -> new CommandException(ChatMessage.createTextMessage("mod not found"))); + ChatMessage builder = ChatMessage.createTextMessage(""); + builder.addText("Mod Name: ".concat(container.getMetadata().getName()).concat("\n")); + builder.addText("Description: ".concat(container.getMetadata().getDescription()).concat("\n")); + ContactInformation contact = container.getMetadata().getContact(); + + if (contact.get("issues").isPresent()) { + ChatMessage issueText = ChatMessage.createTextMessage(""); + issueText.addText("Issues: "); + ChatMessage issueUrl = ChatMessage.createTextMessage(contact.get("issues").get()); + issueText.addUsing(issueUrl); + issueText.addText("\n"); + builder.addUsing(issueText); + } + + if (contact.get("sources").isPresent()) { + ChatMessage sourcesText = ChatMessage.createTextMessage(""); + sourcesText.addText("Sources: "); + ChatMessage sourcesUrl = ChatMessage.createTextMessage(contact.get("sources").get()); + sourcesText.addUsing(sourcesUrl); + sourcesText.addText("\n"); + builder.addUsing(sourcesText); + } + + builder.addText("Metadata Type: ".concat(container.getMetadata().getType()).concat("\n")); + source.method_5505(builder); + return CommandResult.success(); + } +} diff --git a/legacyfabric-api/1.6.4/src/testmod/java/net/legacyfabric/fabric/test/command/SpongeCommandTest.java b/legacyfabric-api/1.6.4/src/testmod/java/net/legacyfabric/fabric/test/command/SpongeCommandTest.java new file mode 100644 index 000000000..af1a6c161 --- /dev/null +++ b/legacyfabric-api/1.6.4/src/testmod/java/net/legacyfabric/fabric/test/command/SpongeCommandTest.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 - 2024 Legacy Fabric + * Copyright (c) 2016 - 2022 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.legacyfabric.fabric.test.command; + +import net.fabricmc.api.ModInitializer; + +import net.legacyfabric.fabric.api.command.v2.CommandRegistrar; + +public class SpongeCommandTest implements ModInitializer { + @Override + public void onInitialize() { + CommandRegistrar.EVENT.register((manager, dedicated) -> { + ModMetadataCommand.register(manager); + }); + } +} diff --git a/legacyfabric-api/1.6.4/src/testmod/resources/fabric.mod.json b/legacyfabric-api/1.6.4/src/testmod/resources/fabric.mod.json index a51bb913b..de4aaf26a 100644 --- a/legacyfabric-api/1.6.4/src/testmod/resources/fabric.mod.json +++ b/legacyfabric-api/1.6.4/src/testmod/resources/fabric.mod.json @@ -22,6 +22,7 @@ "net.legacyfabric.fabric.test.lifecycle.ServerLifecycleEventsTest", "net.legacyfabric.fabric.test.item.group.ItemGroupTest", "net.legacyfabric.fabric.test.entity.EntityEventsTest", + "net.legacyfabric.fabric.test.command.SpongeCommandTest", "net.legacyfabric.fabric.test.command.CommandV1Test" ] },