cloud.commandframework.fabric.argument.FabricArgumentParsers 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.argument;
import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
import cloud.commandframework.context.CommandContext;
import cloud.commandframework.fabric.FabricCommandContextKeys;
import cloud.commandframework.fabric.FabricCommandManager;
import cloud.commandframework.fabric.data.Coordinates;
import cloud.commandframework.fabric.data.Message;
import cloud.commandframework.fabric.data.MinecraftTime;
import cloud.commandframework.fabric.data.MultipleEntitySelector;
import cloud.commandframework.fabric.data.MultiplePlayerSelector;
import cloud.commandframework.fabric.data.SingleEntitySelector;
import cloud.commandframework.fabric.data.SinglePlayerSelector;
import cloud.commandframework.fabric.internal.EntitySelectorAccess;
import cloud.commandframework.fabric.mixin.MessageArgumentMessageAccess;
import cloud.commandframework.fabric.mixin.MessageArgumentPartAccess;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.class_1297;
import net.minecraft.class_2168;
import net.minecraft.class_2172;
import net.minecraft.class_2186;
import net.minecraft.class_2196;
import net.minecraft.class_2245;
import net.minecraft.class_2262;
import net.minecraft.class_2264;
import net.minecraft.class_2274;
import net.minecraft.class_2277;
import net.minecraft.class_2300;
import net.minecraft.class_2338;
import net.minecraft.class_243;
import net.minecraft.class_2561;
import net.minecraft.class_3222;
import net.minecraft.class_7157;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.ApiStatus;
/**
* Parsers for Vanilla command argument types.
*
* @since 1.5.0
*/
public final class FabricArgumentParsers {
private FabricArgumentParsers() {
}
/**
* A parser that wraps Brigadier argument types which need a {@link class_7157}
*
* @param sender type
* @param argument value type
* @param factory factory that creates these arguments
* @return the parser
*/
public static @NonNull ArgumentParser contextual(final @NonNull Function> factory) {
return new WrappedBrigadierParser<>(new ContextualArgumentTypeProvider<>(factory));
}
/**
* A parser for in-game time, in ticks.
*
* @param sender type
* @return a parser instance
* @since 1.5.0
*/
public static @NonNull ArgumentParser time() {
return new WrappedBrigadierParser(class_2245.method_9489())
.map((ctx, val) -> ArgumentParseResult.success(MinecraftTime.of(val)));
}
/**
* A parser for block coordinates.
*
* @param sender type
* @return a parser instance
*/
public static @NonNull ArgumentParser blockPos() {
return new WrappedBrigadierParser(class_2262.method_9698())
.map(FabricArgumentParsers::mapToCoordinates);
}
/**
* A parser for column coordinates.
*
* @param sender type
* @return a parser instance
*/
public static @NonNull ArgumentParser columnPos() {
return new WrappedBrigadierParser(class_2264.method_9701())
.map(FabricArgumentParsers::mapToCoordinates);
}
/**
* A parser for coordinates, relative or absolute, from 2 doubles for x and z,
* with y always defaulting to 0.
*
* @param centerIntegers whether to center integers at x.5
* @param sender type
* @return a parser instance
*/
public static @NonNull ArgumentParser vec2(final boolean centerIntegers) {
return new WrappedBrigadierParser(new class_2274(
centerIntegers))
.map(FabricArgumentParsers::mapToCoordinates);
}
/**
* A parser for coordinates, relative or absolute, from 3 doubles.
*
* @param centerIntegers whether to center integers at x.5
* @param sender type
* @return a parser instance
*/
public static @NonNull ArgumentParser vec3(final boolean centerIntegers) {
return new WrappedBrigadierParser(class_2277.method_9735(
centerIntegers))
.map(FabricArgumentParsers::mapToCoordinates);
}
@SuppressWarnings("unchecked")
private static @NonNull ArgumentParseResult<@NonNull O> mapToCoordinates(
final @NonNull CommandContext ctx,
final net.minecraft.@NonNull class_2267 posArgument
) {
return requireCommandSourceStack(
ctx,
serverCommandSource -> ArgumentParseResult.success((O) new CoordinatesImpl(
serverCommandSource,
posArgument
))
);
}
/**
* A parser for {@link SinglePlayerSelector}.
*
* @param sender type
* @return a parser instance
* @since 1.5.0
*/
public static @NonNull ArgumentParser singlePlayerSelector() {
return new WrappedBrigadierParser(class_2186.method_9305())
.map((ctx, entitySelector) -> requireCommandSourceStack(
ctx,
serverCommandSource -> handleCommandSyntaxExceptionAsFailure(
() -> ArgumentParseResult.success(new SinglePlayerSelectorImpl(
((EntitySelectorAccess) entitySelector).inputString(),
entitySelector,
entitySelector.method_9811(serverCommandSource)
))
)
));
}
/**
* A parser for {@link MultiplePlayerSelector}.
*
* @param sender type
* @return a parser instance
* @since 1.5.0
*/
public static @NonNull ArgumentParser multiplePlayerSelector() {
return new WrappedBrigadierParser(class_2186.method_9308())
.map((ctx, entitySelector) -> requireCommandSourceStack(
ctx,
serverCommandSource -> handleCommandSyntaxExceptionAsFailure(
() -> ArgumentParseResult.success(new MultiplePlayerSelectorImpl(
((EntitySelectorAccess) entitySelector).inputString(),
entitySelector,
entitySelector.method_9813(serverCommandSource)
))
)
));
}
/**
* A parser for {@link SingleEntitySelector}.
*
* @param sender type
* @return a parser instance
* @since 1.5.0
*/
public static @NonNull ArgumentParser singleEntitySelector() {
return new WrappedBrigadierParser(class_2186.method_9309())
.map((ctx, entitySelector) -> requireCommandSourceStack(
ctx,
serverCommandSource -> handleCommandSyntaxExceptionAsFailure(
() -> ArgumentParseResult.success(new SingleEntitySelectorImpl(
((EntitySelectorAccess) entitySelector).inputString(),
entitySelector,
entitySelector.method_9809(serverCommandSource)
))
)
));
}
/**
* A parser for {@link MultipleEntitySelector}.
*
* @param sender type
* @return a parser instance
* @since 1.5.0
*/
public static @NonNull ArgumentParser multipleEntitySelector() {
return new WrappedBrigadierParser(class_2186.method_9306())
.map((ctx, entitySelector) -> requireCommandSourceStack(
ctx,
serverCommandSource -> handleCommandSyntaxExceptionAsFailure(
() -> ArgumentParseResult.success(new MultipleEntitySelectorImpl(
((EntitySelectorAccess) entitySelector).inputString(),
entitySelector,
Collections.unmodifiableCollection(entitySelector.method_9816(serverCommandSource))
))
)
));
}
/**
* A parser for {@link Message}.
*
* @param sender type
* @return a parser instance
* @since 1.5.0
*/
public static @NonNull ArgumentParser message() {
return new WrappedBrigadierParser(class_2196.method_9340())
.map((ctx, format) -> requireCommandSourceStack(
ctx,
serverCommandSource -> handleCommandSyntaxExceptionAsFailure(
() -> ArgumentParseResult.success(MessageImpl.from(
serverCommandSource,
format,
true
))
)
));
}
@FunctionalInterface
private interface CommandSyntaxExceptionThrowingParseResultSupplier {
@NonNull ArgumentParseResult result() throws CommandSyntaxException;
}
private static @NonNull ArgumentParseResult handleCommandSyntaxExceptionAsFailure(
final @NonNull CommandSyntaxExceptionThrowingParseResultSupplier resultSupplier
) {
try {
return resultSupplier.result();
} catch (final CommandSyntaxException ex) {
return ArgumentParseResult.failure(ex);
}
}
private static @NonNull IllegalStateException serverOnly() {
return new IllegalStateException("This command argument type is server-only.");
}
private static @NonNull ArgumentParseResult requireCommandSourceStack(
final @NonNull CommandContext context,
final @NonNull Function> resultFunction
) {
final class_2172 nativeSource = context.get(FabricCommandContextKeys.NATIVE_COMMAND_SOURCE);
if (!(nativeSource instanceof class_2168)) {
return ArgumentParseResult.failure(serverOnly());
}
return resultFunction.apply((class_2168) nativeSource);
}
static final class MessageImpl implements Message {
private final Collection mentionedEntities;
private final class_2561 contents;
static MessageImpl from(
final @NonNull class_2168 source,
final class_2196.@NonNull class_2197 message,
final boolean useSelectors
) throws CommandSyntaxException {
final class_2561 contents = message.method_9341(source, useSelectors);
final class_2196.class_2198[] selectors =
((MessageArgumentMessageAccess) message).accessor$parts();
final Collection entities;
if (!useSelectors || selectors.length == 0) {
entities = Collections.emptySet();
} else {
entities = new HashSet<>();
for (final class_2196.class_2198 selector : selectors) {
entities.addAll(((MessageArgumentPartAccess) selector)
.accessor$selector()
.method_9816(source));
}
}
return new MessageImpl(entities, contents);
}
MessageImpl(final Collection mentionedEntities, final class_2561 contents) {
this.mentionedEntities = mentionedEntities;
this.contents = contents;
}
@Override
public @NonNull Collection mentionedEntities() {
return this.mentionedEntities;
}
@Override
public @NonNull class_2561 contents() {
return this.contents;
}
}
static final class CoordinatesImpl implements Coordinates,
Coordinates.CoordinatesXZ,
Coordinates.BlockCoordinates,
Coordinates.ColumnCoordinates {
private final class_2168 source;
private final net.minecraft.class_2267 posArgument;
CoordinatesImpl(
final @NonNull class_2168 source,
final net.minecraft.@NonNull class_2267 posArgument
) {
this.source = source;
this.posArgument = posArgument;
}
@Override
public @NonNull class_243 position() {
return this.posArgument.method_9708(this.source);
}
@Override
public @NonNull class_2338 blockPos() {
return class_2338.method_49638(this.position());
}
@Override
public boolean isXRelative() {
return this.posArgument.method_9705();
}
@Override
public boolean isYRelative() {
return this.posArgument.method_9706();
}
@Override
public boolean isZRelative() {
return this.posArgument.method_9707();
}
@Override
public net.minecraft.@NonNull class_2267 wrappedCoordinates() {
return this.posArgument;
}
}
static final class SingleEntitySelectorImpl implements SingleEntitySelector {
private final String inputString;
private final class_2300 entitySelector;
private final class_1297 selectedEntity;
SingleEntitySelectorImpl(
final @NonNull String inputString,
final @NonNull class_2300 entitySelector,
final @NonNull class_1297 selectedEntity
) {
this.inputString = inputString;
this.entitySelector = entitySelector;
this.selectedEntity = selectedEntity;
}
@Override
public @NonNull String inputString() {
return this.inputString;
}
@Override
public @NonNull class_2300 selector() {
return this.entitySelector;
}
@Override
public @NonNull class_1297 getSingle() {
return this.selectedEntity;
}
}
static final class MultipleEntitySelectorImpl implements MultipleEntitySelector {
private final String inputString;
private final class_2300 entitySelector;
private final Collection selectedEntities;
MultipleEntitySelectorImpl(
final @NonNull String inputString,
final @NonNull class_2300 entitySelector,
final @NonNull Collection selectedEntities
) {
this.inputString = inputString;
this.entitySelector = entitySelector;
this.selectedEntities = selectedEntities;
}
@Override
public @NonNull String inputString() {
return this.inputString;
}
@Override
public @NonNull class_2300 selector() {
return this.entitySelector;
}
@Override
public @NonNull Collection get() {
return this.selectedEntities;
}
}
static final class SinglePlayerSelectorImpl implements SinglePlayerSelector {
private final String inputString;
private final class_2300 entitySelector;
private final class_3222 selectedPlayer;
SinglePlayerSelectorImpl(
final @NonNull String inputString,
final @NonNull class_2300 entitySelector,
final @NonNull class_3222 selectedPlayer
) {
this.inputString = inputString;
this.entitySelector = entitySelector;
this.selectedPlayer = selectedPlayer;
}
@Override
public @NonNull String inputString() {
return this.inputString;
}
@Override
public @NonNull class_2300 selector() {
return this.entitySelector;
}
@Override
public @NonNull class_3222 getSingle() {
return this.selectedPlayer;
}
}
static final class MultiplePlayerSelectorImpl implements MultiplePlayerSelector {
private final String inputString;
private final class_2300 entitySelector;
private final Collection selectedPlayers;
MultiplePlayerSelectorImpl(
final @NonNull String inputString,
final @NonNull class_2300 entitySelector,
final @NonNull Collection selectedPlayers
) {
this.inputString = inputString;
this.entitySelector = entitySelector;
this.selectedPlayers = selectedPlayers;
}
@Override
public @NonNull String inputString() {
return this.inputString;
}
@Override
public @NonNull class_2300 selector() {
return this.entitySelector;
}
@Override
public @NonNull Collection get() {
return this.selectedPlayers;
}
}
@ApiStatus.Internal
public static final class ContextualArgumentTypeProvider implements Supplier> {
private static final ThreadLocal CONTEXT = new ThreadLocal<>();
private static final Map, Set>> INSTANCES =
new WeakHashMap<>();
private final Function> provider;
private volatile ArgumentType provided;
/**
* Temporarily expose a command build context to providers called from this thread.
*
* @param ctx the context
* @param commandManager command manager to use
* @param resetExisting whether to clear cached state from existing provider instances for this command type
* @param action an action to perform while the context is exposed
* @since 1.7.0
*/
public static void withBuildContext(
final FabricCommandManager, ?> commandManager,
final class_7157 ctx,
final boolean resetExisting,
final Runnable action
) {
final ThreadLocalContext context = new ThreadLocalContext(commandManager, ctx);
CONTEXT.set(context);
try {
if (resetExisting) {
synchronized (INSTANCES) {
for (final ContextualArgumentTypeProvider> contextualArgumentTypeProvider : context.instances()) {
contextualArgumentTypeProvider.provided = null;
}
}
}
action.run();
} finally {
CONTEXT.remove();
}
}
private static final class ThreadLocalContext {
private final FabricCommandManager, ?> commandManager;
private final class_7157 commandBuildContext;
private ThreadLocalContext(
final FabricCommandManager, ?> commandManager,
final class_7157 commandBuildContext
) {
this.commandManager = commandManager;
this.commandBuildContext = commandBuildContext;
}
private Set> instances() {
return INSTANCES.computeIfAbsent(this.commandManager, $ -> Collections.newSetFromMap(new WeakHashMap<>()));
}
}
ContextualArgumentTypeProvider(final @NonNull Function> provider) {
this.provider = provider;
}
@Override
public ArgumentType get() {
final ThreadLocalContext ctx = CONTEXT.get();
if (ctx != null) {
synchronized (INSTANCES) {
ctx.instances().add(this);
}
}
ArgumentType provided = this.provided;
if (provided == null) {
synchronized (this) {
if (this.provided == null) {
if (ctx == null) {
throw new IllegalStateException(
"No build context was available while trying to compute an argument type");
}
provided = this.provider.apply(ctx.commandBuildContext);
this.provided = provided;
}
}
}
return provided;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy