All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cloud.commandframework.fabric.FabricCommandManager Maven / Gradle / Ivy

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

import cloud.commandframework.CommandManager;
import cloud.commandframework.CommandTree;
import cloud.commandframework.arguments.standard.UUIDArgument;
import cloud.commandframework.brigadier.BrigadierManagerHolder;
import cloud.commandframework.brigadier.CloudBrigadierManager;
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
import cloud.commandframework.context.CommandContext;
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.execution.FilteringCommandSuggestionProcessor;
import cloud.commandframework.fabric.argument.FabricArgumentParsers;
import cloud.commandframework.fabric.argument.RegistryEntryArgument;
import cloud.commandframework.fabric.argument.TeamArgument;
import cloud.commandframework.fabric.data.MinecraftTime;
import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta;
import cloud.commandframework.permission.PredicatePermission;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.serialization.Codec;
import io.leangen.geantyref.GenericTypeReflector;
import io.leangen.geantyref.TypeToken;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.class_124;
import net.minecraft.class_2096;
import net.minecraft.class_2172;
import net.minecraft.class_2177;
import net.minecraft.class_2179;
import net.minecraft.class_2183;
import net.minecraft.class_2196;
import net.minecraft.class_2203;
import net.minecraft.class_2212;
import net.minecraft.class_2216;
import net.minecraft.class_2218;
import net.minecraft.class_2223;
import net.minecraft.class_2224;
import net.minecraft.class_2232;
import net.minecraft.class_2252;
import net.minecraft.class_2273;
import net.minecraft.class_2287;
import net.minecraft.class_2290;
import net.minecraft.class_2350;
import net.minecraft.class_2394;
import net.minecraft.class_2487;
import net.minecraft.class_2520;
import net.minecraft.class_268;
import net.minecraft.class_274;
import net.minecraft.class_2960;
import net.minecraft.class_5242;
import net.minecraft.class_5321;
import net.minecraft.class_5473;
import net.minecraft.class_7079;
import net.minecraft.class_7157;
import net.minecraft.class_7924;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;

/**
 * A command manager for either the server or client on Fabric.
 *
 * 

Commands registered with managers of this type will be registered into a Brigadier command tree.

* *

Where possible, Vanilla argument types are made available in a cloud-friendly format. In some cases, these argument * types may only be available for server commands. Mod-provided argument types can be exposed to Cloud as well, by using * {@link WrappedBrigadierParser}.

* * @param the manager's sender type * @param the platform sender type * @see FabricServerCommandManager for server commands * @since 1.5.0 */ public abstract class FabricCommandManager extends CommandManager implements BrigadierManagerHolder { private static final Logger LOGGER = LogManager.getLogger(); private static final int MOD_PUBLIC_STATIC_FINAL = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; private final Function commandSourceMapper; private final Function backwardsCommandSourceMapper; private final CloudBrigadierManager brigadierManager; /** * Create a new command manager instance. * * @param commandExecutionCoordinator Execution coordinator instance. The coordinator is in charge of executing incoming * commands. Some considerations must be made when picking a suitable execution coordinator * for your platform. For example, an entirely asynchronous coordinator is not suitable * when the parsers used in that particular platform are not thread safe. If you have * commands that perform blocking operations, however, it might not be a good idea to * use a synchronous execution coordinator. In most cases you will want to pick between * {@link CommandExecutionCoordinator#simpleCoordinator()} and * {@link AsynchronousCommandExecutionCoordinator} * @param commandSourceMapper Function that maps {@link class_2172} to the command sender type * @param backwardsCommandSourceMapper Function that maps the command sender type to {@link class_2172} * @param registrationHandler the handler accepting command registrations * @param dummyCommandSourceProvider a provider of a dummy command source, for use with brigadier registration * @since 1.5.0 */ @SuppressWarnings("unchecked") FabricCommandManager( final @NonNull Function<@NonNull CommandTree, @NonNull CommandExecutionCoordinator> commandExecutionCoordinator, final @NonNull Function commandSourceMapper, final @NonNull Function backwardsCommandSourceMapper, final @NonNull FabricCommandRegistrationHandler registrationHandler, final @NonNull Supplier dummyCommandSourceProvider ) { super(commandExecutionCoordinator, registrationHandler); this.commandSourceMapper = commandSourceMapper; this.backwardsCommandSourceMapper = backwardsCommandSourceMapper; // We're always brigadier this.brigadierManager = new CloudBrigadierManager<>(this, () -> new CommandContext<>( // This looks ugly, but it's what the server does when loading datapack functions in 1.16+ // See net.minecraft.server.function.FunctionLoader.reload for reference this.commandSourceMapper.apply(dummyCommandSourceProvider.get()), this )); this.brigadierManager.backwardsBrigadierSenderMapper(this.backwardsCommandSourceMapper); this.brigadierManager.brigadierSenderMapper(this.commandSourceMapper); this.registerNativeBrigadierMappings(this.brigadierManager); this.captionRegistry(new FabricCaptionRegistry<>()); this.registerCommandPreProcessor(new FabricCommandPreprocessor<>(this)); this.commandSuggestionProcessor(new FilteringCommandSuggestionProcessor<>( FilteringCommandSuggestionProcessor.Filter.startsWith(true).andTrimBeforeLastSpace() )); ((FabricCommandRegistrationHandler) this.commandRegistrationHandler()).initialize(this); } private void registerNativeBrigadierMappings(final @NonNull CloudBrigadierManager brigadier) { /* Cloud-native argument types */ brigadier.registerMapping(new TypeToken>() { }, builder -> builder.toConstant(class_5242.method_27643())); this.registerRegistryEntryMappings(); brigadier.registerMapping(new TypeToken>() { }, builder -> builder.toConstant(net.minecraft.class_2243.method_9482())); this.parserRegistry().registerParserSupplier( TypeToken.get(class_268.class), params -> new TeamArgument.TeamParser<>() ); /* Wrapped/Constant Brigadier types, native value type */ this.registerConstantNativeParserSupplier(class_124.class, class_2177.method_9276()); this.registerConstantNativeParserSupplier(class_2487.class, class_2179.method_9284()); this.registerConstantNativeParserSupplier(class_2520.class, class_2212.method_9389()); this.registerConstantNativeParserSupplier(class_2203.class_2209.class, class_2203.method_9360()); this.registerConstantNativeParserSupplier(class_274.class, class_2216.method_9399()); this.registerConstantNativeParserSupplier(class_2218.class_2219.class, class_2218.method_9404()); this.registerConstantNativeParserSupplier(class_5473.class_5474.class, class_5473.method_30658()); this.registerConstantNativeParserSupplier(new TypeToken>() { }, class_2273.method_9721()); this.registerConstantNativeParserSupplier(class_2960.class, class_2232.method_9441()); this.registerConstantNativeParserSupplier(class_2183.class_2184.class, class_2183.method_9295()); this.registerConstantNativeParserSupplier(class_2096.class_2100.class, class_2224.method_9422()); this.registerConstantNativeParserSupplier(class_2096.class_2099.class, class_2224.method_30918()); this.registerContextualNativeParserSupplier(class_2394.class, class_2223::method_9417); this.registerContextualNativeParserSupplier(class_2290.class, class_2287::method_9776); this.registerContextualNativeParserSupplier(class_2252.class_2254.class, class_2252::method_9645); /* Wrapped/Constant Brigadier types, mapped value type */ this.registerConstantNativeParserSupplier(class_2196.class_2197.class, class_2196.method_9340()); this.parserRegistry().registerParserSupplier( TypeToken.get(MinecraftTime.class), params -> FabricArgumentParsers.time() ); } @SuppressWarnings({"unchecked", "rawtypes"}) private void registerRegistryEntryMappings() { this.brigadierManager.registerMapping( new TypeToken>() { }, builder -> { builder.to(argument -> class_7079.method_41224((class_5321) argument.registryKey())); } ); /* Find all fields of RegistryKey> and register those */ /* This only works for vanilla registries really, we'll have to do other things for non-vanilla ones */ final Set> seenClasses = new HashSet<>(); /* Some registries have types that are too generic... we'll skip those for now. * Eventually, these could be resolved by using ParserParameters in some way? */ seenClasses.add(class_2960.class); seenClasses.add(Codec.class); seenClasses.add(String.class); // avoid pottery pattern registry overriding default string parser for (final Field field : class_7924.class.getDeclaredFields()) { if ((field.getModifiers() & MOD_PUBLIC_STATIC_FINAL) != MOD_PUBLIC_STATIC_FINAL) { continue; } if (!field.getType().equals(class_5321.class)) { continue; } final Type generic = field.getGenericType(); /* RegistryKey> */ if (!(generic instanceof ParameterizedType)) { continue; } Type registryType = ((ParameterizedType) generic).getActualTypeArguments()[0]; while (registryType instanceof WildcardType) { registryType = ((WildcardType) registryType).getUpperBounds()[0]; } if (!(registryType instanceof ParameterizedType)) { /* expected: Registry */ continue; } final class_5321 key; try { key = (class_5321) field.get(null); } catch (final IllegalAccessException ex) { LOGGER.warn("Failed to access value of registry key in field {} of type {}", field.getName(), generic, ex); continue; } final Type valueType = ((ParameterizedType) registryType).getActualTypeArguments()[0]; if (seenClasses.contains(GenericTypeReflector.erase(valueType))) { LOGGER.debug("Encountered duplicate type in registry {}: type {}", key, valueType); continue; } seenClasses.add(GenericTypeReflector.erase(valueType)); /* and now, finally, we can register */ this.parserRegistry().registerParserSupplier( TypeToken.get(valueType), params -> new RegistryEntryArgument.Parser(key) ); } } /** * Register a parser supplier for a brigadier type that has no options and whose output can be directly used. * * @param type the Java type to map * @param argument a function providing the Brigadier parser given a build context * @param value type * @since 1.7.0 */ final void registerContextualNativeParserSupplier( final @NonNull Class type, final @NonNull Function> argument ) { this.parserRegistry().registerParserSupplier( TypeToken.get(type), params -> FabricArgumentParsers.contextual(argument) ); } /** * Register a parser supplier for a brigadier type that has no options and whose output can be directly used. * * @param type the Java type to map * @param argument the Brigadier parser * @param value type * @since 1.5.0 */ final void registerConstantNativeParserSupplier(final @NonNull Class type, final @NonNull ArgumentType argument) { this.registerConstantNativeParserSupplier(TypeToken.get(type), argument); } /** * Register a parser supplier for a brigadier type that has no options and whose output can be directly used. * * @param type the Java type to map * @param argument the Brigadier parser * @param value type * @since 1.5.0 */ final void registerConstantNativeParserSupplier( final @NonNull TypeToken type, final @NonNull ArgumentType argument ) { this.parserRegistry().registerParserSupplier(type, params -> new WrappedBrigadierParser<>(argument)); } @Override public final @NonNull CommandMeta createDefaultCommandMeta() { return SimpleCommandMeta.empty(); } /** * Gets the mapper from a game {@link class_2172} to the manager's {@code C} type. * * @return Command source mapper * @since 1.5.0 */ public final @NonNull Function<@NonNull S, @NonNull C> commandSourceMapper() { return this.commandSourceMapper; } /** * Gets the mapper from the manager's {@code C} type to a game {@link class_2172}. * * @return Command source mapper * @since 1.5.0 */ public final @NonNull Function<@NonNull C, @NonNull S> backwardsCommandSourceMapper() { return this.backwardsCommandSourceMapper; } @Override public final @NonNull CloudBrigadierManager brigadierManager() { return this.brigadierManager; } /* transition state to prevent further registration */ final void registrationCalled() { this.lockRegistration(); } /** * Get a permission predicate which passes when the sender has the specified permission level. * * @param permissionLevel permission level to require * @return a permission predicate * @since 1.5.0 */ public @NonNull PredicatePermission permissionLevel(final int permissionLevel) { return sender -> this.backwardsCommandSourceMapper() .apply(sender) .method_9259(permissionLevel); } }