cloud.commandframework.fabric.FabricCommandManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cloud-fabric Show documentation
Show all versions of cloud-fabric Show documentation
Command framework and dispatcher for the JVM
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 extends Registry>> 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 extends Registry>> */
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);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy