Files
fabric-carpet/src/main/java/carpet/script/command/CommandArgument.java
T
2026-05-10 21:38:16 +02:00

1301 lines
51 KiB
Java

package carpet.script.command;
import carpet.script.CarpetScriptHost;
import carpet.script.CarpetScriptServer;
import carpet.script.argument.FunctionArgument;
import carpet.script.external.Carpet;
import carpet.script.external.Vanilla;
import carpet.script.value.BlockValue;
import carpet.script.value.BooleanValue;
import carpet.script.value.EntityValue;
import carpet.script.value.FormattedTextValue;
import carpet.script.value.ListValue;
import carpet.script.value.MapValue;
import carpet.script.value.NBTSerializableValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.script.value.ValueConversions;
import com.mojang.datafixers.util.Either;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.arguments.DoubleArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.LongArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.ParsedCommandNode;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.CommandNode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import net.minecraft.ChatFormatting;
import net.minecraft.advancements.predicates.MinMaxBounds;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.arguments.AngleArgument;
import net.minecraft.commands.arguments.TeamColorArgument;
import net.minecraft.commands.arguments.CompoundTagArgument;
import net.minecraft.commands.arguments.DimensionArgument;
import net.minecraft.commands.arguments.EntityAnchorArgument;
import net.minecraft.commands.arguments.GameProfileArgument;
import net.minecraft.commands.arguments.MessageArgument;
import net.minecraft.commands.arguments.NbtPathArgument;
import net.minecraft.commands.arguments.NbtTagArgument;
import net.minecraft.commands.arguments.ObjectiveArgument;
import net.minecraft.commands.arguments.ObjectiveCriteriaArgument;
import net.minecraft.commands.arguments.ParticleArgument;
import net.minecraft.commands.arguments.RangeArgument;
import net.minecraft.commands.arguments.ResourceArgument;
import net.minecraft.commands.arguments.IdentifierArgument;
import net.minecraft.commands.arguments.ResourceOrTagArgument;
import net.minecraft.commands.arguments.ScoreHolderArgument;
import net.minecraft.commands.arguments.ScoreboardSlotArgument;
import net.minecraft.commands.arguments.TeamArgument;
import net.minecraft.commands.arguments.TimeArgument;
import net.minecraft.commands.arguments.UuidArgument;
import net.minecraft.commands.arguments.blocks.BlockInput;
import net.minecraft.commands.arguments.blocks.BlockPredicateArgument;
import net.minecraft.commands.arguments.blocks.BlockStateArgument;
import net.minecraft.commands.arguments.coordinates.ColumnPosArgument;
import net.minecraft.commands.arguments.coordinates.RotationArgument;
import net.minecraft.commands.arguments.coordinates.SwizzleArgument;
import net.minecraft.commands.arguments.coordinates.Vec2Argument;
import net.minecraft.commands.arguments.coordinates.Vec3Argument;
import net.minecraft.commands.arguments.item.ItemArgument;
import net.minecraft.commands.synchronization.SuggestionProviders;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.Identifier;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.commands.BossBarCommands;
import net.minecraft.server.commands.LootCommand;
import net.minecraft.server.players.NameAndId;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.scores.ScoreHolder;
import net.minecraft.world.scores.TeamColor;
import org.jspecify.annotations.Nullable;
import static net.minecraft.commands.Commands.argument;
public abstract class CommandArgument
{
public static CommandSyntaxException error(String text)
{
return new SimpleCommandExceptionType(Component.literal(text)).create();
}
private static final List<? extends CommandArgument> baseTypes = Lists.newArrayList(
// default
new StringArgument(),
// vanilla arguments as per https://minecraft.wiki/w/Argument_types
new VanillaUnconfigurableArgument("bool", BoolArgumentType::bool,
(c, p) -> BooleanValue.of(BoolArgumentType.getBool(c, p)), false
),
new FloatArgument(),
new IntArgument(),
new WordArgument(), new GreedyStringArgument(),
new VanillaUnconfigurableArgument("yaw", AngleArgument::angle, // angle
(c, p) -> new NumericValue(AngleArgument.getAngle(c, p)), true
),
new BlockPosArgument(),
new VanillaUnconfigurableArgument("block", BlockStateArgument::block,
(c, p) -> {
BlockInput result = BlockStateArgument.getBlock(c, p);
return new BlockValue(result.getState(), c.getSource().getLevel(), Vanilla.BlockInput_getTag(result));
},
param -> (ctx, builder) -> ctx.getArgument(param, BlockStateArgument.class).listSuggestions(ctx, builder)
),
new VanillaUnconfigurableArgument("blockpredicate", BlockPredicateArgument::blockPredicate,
(c, p) -> ValueConversions.ofBlockPredicate(c.getSource().getServer().registryAccess(), BlockPredicateArgument.getBlockPredicate(c, p)),
param -> (ctx, builder) -> ctx.getArgument(param, BlockPredicateArgument.class).listSuggestions(ctx, builder)
),
new VanillaUnconfigurableArgument("teamcolor", TeamColorArgument::teamColor,
(c, p) -> {
TeamColor format = TeamColorArgument.getTeamColor(c, p);
return ListValue.of(StringValue.of(format.getSerializedName()), ValueConversions.ofRGB(format.rgb()));
},
false
),
new VanillaUnconfigurableArgument("columnpos", ColumnPosArgument::columnPos,
(c, p) -> ValueConversions.of(ColumnPosArgument.getColumnPos(c, p)), false
),
// component // raw json
new VanillaUnconfigurableArgument("dimension", DimensionArgument::dimension,
(c, p) -> ValueConversions.of(DimensionArgument.getDimension(c, p)), false
),
new EntityArgument(),
new VanillaUnconfigurableArgument("anchor", EntityAnchorArgument::anchor,
(c, p) -> StringValue.of(EntityAnchorArgument.getAnchor(c, p).name()), false
),
new VanillaUnconfigurableArgument("entitytype", c -> ResourceArgument.resource(c, Registries.ENTITY_TYPE),
(c, p) -> ValueConversions.of(ResourceArgument.getSummonableEntityType(c, p).key()), SuggestionProviders.cast(SuggestionProviders.SUMMONABLE_ENTITIES)
),
new VanillaUnconfigurableArgument("floatrange", RangeArgument::floatRange,
(c, p) -> ValueConversions.of(c.getArgument(p, MinMaxBounds.Doubles.class)), true
),
// function??
new PlayerProfileArgument(),
new VanillaUnconfigurableArgument("intrange", RangeArgument::intRange,
(c, p) -> ValueConversions.of(RangeArgument.Ints.getRange(c, p)), true
),
new VanillaUnconfigurableArgument("enchantment", Registries.ENCHANTMENT),
// item_predicate ?? //same as item but accepts tags, not sure right now
new SlotArgument(),
new VanillaUnconfigurableArgument("item", ItemArgument::item,
(c, p) -> ValueConversions.of(ItemArgument.getItem(c, p).createItemStack(1), c.getSource().registryAccess()),
param -> (ctx, builder) -> ctx.getArgument(param, ItemArgument.class).listSuggestions(ctx, builder)
),
new VanillaUnconfigurableArgument("message", MessageArgument::message,
(c, p) -> new FormattedTextValue(MessageArgument.getMessage(c, p)), true
),
new VanillaUnconfigurableArgument("effect", Registries.MOB_EFFECT),
new TagArgument(), // for nbt_compound_tag and nbt_tag
new VanillaUnconfigurableArgument("path", NbtPathArgument::nbtPath,
(c, p) -> StringValue.of(NbtPathArgument.getPath(c, p).toString()), true
),
new VanillaUnconfigurableArgument("objective", ObjectiveArgument::objective,
(c, p) -> ValueConversions.of(ObjectiveArgument.getObjective(c, p)), false
),
new VanillaUnconfigurableArgument("criterion", ObjectiveCriteriaArgument::criteria,
(c, p) -> StringValue.of(ObjectiveCriteriaArgument.getCriteria(c, p).getName()), false
),
// operation // not sure if we need it, you have scarpet for that
new VanillaUnconfigurableArgument("particle", ParticleArgument::particle,
(c, p) -> ValueConversions.of(ParticleArgument.getParticle(c, p), c.getSource().registryAccess()), (c, b) -> SharedSuggestionProvider.suggestResource(c.getSource().getServer().registryAccess().lookupOrThrow(Registries.PARTICLE_TYPE).keySet(), b)
),
// resource / identifier section
new VanillaUnconfigurableArgument("recipe", Registries.RECIPE),
new VanillaUnconfigurableArgument("advancement", Registries.ADVANCEMENT),
new VanillaUnconfigurableArgument("lootcondition", Registries.LOOT_CONDITION_TYPE),
new VanillaUnconfigurableArgument("loottable", Registries.LOOT_TABLE),
new VanillaUnconfigurableArgument("attribute", Registries.ATTRIBUTE),
new VanillaUnconfigurableArgument("boss", IdentifierArgument::id,
(c, p) -> ValueConversions.of(IdentifierArgument.getId(c, p)), BossBarCommands.SUGGEST_BOSS_BAR
),
new VanillaUnconfigurableArgument("biome", c -> ResourceOrTagArgument.resourceOrTag(c, Registries.BIOME),
(c, p) -> {
ResourceOrTagArgument.Result<Biome> result = ResourceOrTagArgument.getResourceOrTag(c, "biome", Registries.BIOME);
Either<Holder.Reference<Biome>, HolderSet.Named<Biome>> res = result.unwrap();
if (res.left().isPresent())
{
return ValueConversions.of(res.left().get().key());
}
if (res.right().isPresent())
{
return ValueConversions.of(res.right().get().key());
}
return Value.NULL;
}, (ctx, builder) -> SharedSuggestionProvider.suggestResource(ctx.getSource().getServer().registryAccess().lookupOrThrow(Registries.BIOME).keySet(), builder)
),
new VanillaUnconfigurableArgument("sound", IdentifierArgument::id,
(c, p) -> ValueConversions.of(IdentifierArgument.getId(c, p)), SuggestionProviders.cast(SuggestionProviders.AVAILABLE_SOUNDS)
),
new VanillaUnconfigurableArgument("storekey", IdentifierArgument::id,
(c, p) -> ValueConversions.of(IdentifierArgument.getId(c, p)), (ctx, builder) -> SharedSuggestionProvider.suggestResource(ctx.getSource().getServer().getCommandStorage().keys(), builder)
),
// default
new CustomIdentifierArgument(),
// end resource / identifier // I would be great if you guys have suggestions for that.
new VanillaUnconfigurableArgument("rotation",
RotationArgument::rotation,
(c, p) -> {
Vec2 rot = RotationArgument.getRotation(c, p).getRotation(c.getSource());
return ListValue.of(new NumericValue(rot.x), new NumericValue(rot.y));
},
true
),
new ScoreholderArgument(),
new VanillaUnconfigurableArgument("scoreboardslot", ScoreboardSlotArgument::displaySlot,
(c, p) -> StringValue.of(ScoreboardSlotArgument.getDisplaySlot(c, p).getSerializedName()), false
),
new VanillaUnconfigurableArgument("swizzle", SwizzleArgument::swizzle,
(c, p) -> StringValue.of(SwizzleArgument.getSwizzle(c, p).stream().map(Direction.Axis::getSerializedName).collect(Collectors.joining())), true
),
new VanillaUnconfigurableArgument("team", TeamArgument::team,
(c, p) -> StringValue.of(TeamArgument.getTeam(c, p).getName()), false
),
new VanillaUnconfigurableArgument("time", TimeArgument::time,
(c, p) -> new NumericValue(IntegerArgumentType.getInteger(c, p)), false
),
new VanillaUnconfigurableArgument("uuid", UuidArgument::uuid,
(c, p) -> StringValue.of(UuidArgument.getUuid(c, p).toString()), false
),
new VanillaUnconfigurableArgument("surfacelocation", Vec2Argument::vec2,
(c, p) -> {
Vec2 res = Vec2Argument.getVec2(c, p);
return ListValue.of(NumericValue.of(res.x), NumericValue.of(res.y));
},
false
),
new LocationArgument()
);
public static final Map<String, CommandArgument> builtIns = baseTypes.stream().collect(Collectors.toMap(CommandArgument::getTypeSuffix, a -> a));
public static final CommandArgument DEFAULT = baseTypes.get(0);
public static CommandArgument getTypeForArgument(String argument, CarpetScriptHost host)
{
String[] components = argument.split("_");
CommandArgument arg;
for (int i = 0; i < components.length; i++)
{
String candidate = String.join("_", Arrays.asList(components).subList(i, components.length));
arg = host.appArgTypes.get(candidate);
if (arg != null)
{
return arg;
}
arg = builtIns.get(candidate);
if (arg != null)
{
return arg;
}
}
return DEFAULT;
}
public static RequiredArgumentBuilder<CommandSourceStack, ?> argumentNode(String param, CarpetScriptHost host) throws CommandSyntaxException
{
CommandArgument arg = getTypeForArgument(param, host);
if (arg.suggestionProvider != null)
{
return argument(param, arg.getArgumentType(host)).suggests(arg.suggestionProvider.apply(param));
}
if (!arg.needsMatching)
{
return argument(param, arg.getArgumentType(host));
}
String hostName = host.getName();
CarpetScriptServer scriptServer = host.scriptServer();
return argument(param, arg.getArgumentType(host)).suggests((ctx, b) -> {
CarpetScriptHost cHost = scriptServer.modules.get(hostName).retrieveOwnForExecution(ctx.getSource());
return arg.suggest(ctx, b, cHost);
});
}
protected String suffix;
@Nullable
protected Collection<String> examples;
protected boolean needsMatching;
protected boolean caseSensitive = true;
protected Function<String, SuggestionProvider<CommandSourceStack>> suggestionProvider;
protected FunctionArgument customSuggester;
protected CommandArgument(
String suffix,
@Nullable Collection<String> examples,
boolean suggestFromExamples)
{
this.suffix = suffix;
this.examples = examples;
this.needsMatching = suggestFromExamples;
}
protected abstract ArgumentType<?> getArgumentType(CarpetScriptHost host) throws CommandSyntaxException;
public static Value getValue(CommandContext<CommandSourceStack> context, String param, CarpetScriptHost host) throws CommandSyntaxException
{
return getTypeForArgument(param, host).getValueFromContext(context, param);
}
protected abstract Value getValueFromContext(CommandContext<CommandSourceStack> context, String param) throws CommandSyntaxException;
public String getTypeSuffix()
{
return suffix;
}
public static CommandArgument buildFromConfig(String suffix, Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
if (!config.containsKey("type"))
{
throw CommandArgument.error("Custom type " + suffix + " should at least specify the type");
}
String baseType = config.get("type").getString();
if (!builtIns.containsKey(baseType))
{
throw CommandArgument.error("Unknown base type " + baseType + " for custom type " + suffix);
}
CommandArgument variant = builtIns.get(baseType).factory(host.scriptServer().server).get();
variant.suffix = suffix;
variant.configure(config, host);
return variant;
}
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
caseSensitive = config.getOrDefault("case_sensitive", Value.TRUE).getBoolean();
if (config.containsKey("suggester"))
{
customSuggester = FunctionArgument.fromCommandSpec(host, config.get("suggester"));
}
if (config.containsKey("suggest"))
{
if (config.containsKey("suggester"))
{
throw error("Attempted to provide 'suggest' list while 'suggester' is present" + " for custom type " + suffix);
}
Value suggestionValue = config.get("suggest");
if (!(suggestionValue instanceof ListValue))
{
throw error("Argument suggestions needs to be a list" + " for custom type " + suffix);
}
examples = ((ListValue) suggestionValue).getItems().stream()
.map(Value::getString)
.collect(Collectors.toSet());
if (!examples.isEmpty())
{
needsMatching = true;
}
}
}
public CompletableFuture<Suggestions> suggest(
CommandContext<CommandSourceStack> context,
SuggestionsBuilder suggestionsBuilder,
CarpetScriptHost host
) throws CommandSyntaxException
{
String prefix = suggestionsBuilder.getRemaining();
if (!caseSensitive)
{
prefix = prefix.toLowerCase(Locale.ROOT);
}
suggestFor(context, prefix, host).forEach(suggestionsBuilder::suggest);
return suggestionsBuilder.buildFuture();
}
protected List<String> suggestFor(CommandContext<CommandSourceStack> context, String prefix, CarpetScriptHost host) throws CommandSyntaxException
{
return getOptions(context, host).stream().filter(s -> optionMatchesPrefix(prefix, s)).collect(Collectors.toList());
}
protected Collection<String> getOptions(CommandContext<CommandSourceStack> context, CarpetScriptHost host) throws CommandSyntaxException
{
if (customSuggester != null)
{
Runnable currentSection = Carpet.startProfilerSection("Scarpet command");
Map<Value, Value> params = new HashMap<>();
for (ParsedCommandNode<CommandSourceStack> pnode : context.getNodes())
{
CommandNode<CommandSourceStack> node = pnode.getNode();
if (node instanceof ArgumentCommandNode)
{
params.put(StringValue.of(node.getName()), CommandArgument.getValue(context, node.getName(), host));
}
}
List<Value> args = new ArrayList<>(customSuggester.args.size() + 1);
args.add(MapValue.wrap(params));
args.addAll(customSuggester.args);
Value response = host.handleCommand(context.getSource(), customSuggester.function, args);
if (!(response instanceof ListValue))
{
throw error("Custom suggester should return a list of options" + " for custom type " + suffix);
}
Collection<String> res = ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList());
currentSection.run();
return res;
}
return needsMatching ? examples : Collections.singletonList("... " + getTypeSuffix());
}
protected boolean optionMatchesPrefix(String prefix, String option)
{
if (!caseSensitive)
{
option = option.toLowerCase(Locale.ROOT);
}
for (int i = 0; !option.startsWith(prefix, i); ++i)
{
i = option.indexOf('_', i);
if (i < 0)
{
return false;
}
}
return true;
}
protected abstract Supplier<CommandArgument> factory(MinecraftServer server);
private static class StringArgument extends CommandArgument
{
Set<String> validOptions = Collections.emptySet();
private StringArgument()
{
super("string", StringArgumentType.StringType.QUOTABLE_PHRASE.getExamples(), true);
}
@Override
public ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return StringArgumentType.string();
}
@Override
public Value getValueFromContext(CommandContext<CommandSourceStack> context, String param) throws CommandSyntaxException
{
String choseValue = StringArgumentType.getString(context, param);
if (!caseSensitive)
{
choseValue = choseValue.toLowerCase(Locale.ROOT);
}
if (!validOptions.isEmpty() && !validOptions.contains(choseValue))
{
throw new SimpleCommandExceptionType(Component.literal("Incorrect value for " + param + ": " + choseValue + " for custom type " + suffix)).create();
}
return StringValue.of(choseValue);
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
if (config.containsKey("options"))
{
Value optionsValue = config.get("options");
if (!(optionsValue instanceof ListValue))
{
throw error("Custom string type requires options passed as a list" + " for custom type " + suffix);
}
validOptions = ((ListValue) optionsValue).getItems().stream()
.map(v -> caseSensitive ? v.getString() : (v.getString().toLowerCase(Locale.ROOT)))
.collect(Collectors.toSet());
}
}
@Override
protected Collection<String> getOptions(CommandContext<CommandSourceStack> context, CarpetScriptHost host) throws CommandSyntaxException
{
return validOptions.isEmpty() ? super.getOptions(context, host) : validOptions;
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return StringArgument::new;
}
}
private static class WordArgument extends StringArgument
{
private WordArgument()
{
super();
suffix = "term";
examples = StringArgumentType.StringType.SINGLE_WORD.getExamples();
}
@Override
public ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return StringArgumentType.word();
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return WordArgument::new;
}
}
private static class GreedyStringArgument extends StringArgument
{
private GreedyStringArgument()
{
super();
suffix = "text";
examples = StringArgumentType.StringType.GREEDY_PHRASE.getExamples();
}
@Override
public ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return StringArgumentType.greedyString();
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return GreedyStringArgument::new;
}
}
private static class BlockPosArgument extends CommandArgument
{
private boolean mustBeLoaded = false;
private BlockPosArgument()
{
super("pos", net.minecraft.commands.arguments.coordinates.BlockPosArgument.blockPos().getExamples(), false);
}
@Override
public ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return net.minecraft.commands.arguments.coordinates.BlockPosArgument.blockPos();
}
@Override
public Value getValueFromContext(CommandContext<CommandSourceStack> context, String param) throws CommandSyntaxException
{
BlockPos pos = mustBeLoaded
? net.minecraft.commands.arguments.coordinates.BlockPosArgument.getLoadedBlockPos(context, param)
: net.minecraft.commands.arguments.coordinates.BlockPosArgument.getSpawnablePos(context, param);
return ValueConversions.of(pos);
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
mustBeLoaded = config.getOrDefault("loaded", Value.FALSE).getBoolean();
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return BlockPosArgument::new;
}
}
private static class LocationArgument extends CommandArgument
{
boolean blockCentered;
private LocationArgument()
{
super("location", Vec3Argument.vec3().getExamples(), false);
blockCentered = true;
}
@Override
protected ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return Vec3Argument.vec3(blockCentered);
}
@Override
protected Value getValueFromContext(CommandContext<CommandSourceStack> context, String param)
{
return ValueConversions.of(Vec3Argument.getVec3(context, param));
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
blockCentered = config.getOrDefault("block_centered", Value.TRUE).getBoolean();
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return LocationArgument::new;
}
}
private static class EntityArgument extends CommandArgument
{
boolean onlyFans;
boolean single;
private EntityArgument()
{
super("entities", net.minecraft.commands.arguments.EntityArgument.entities().getExamples(), false);
onlyFans = false;
single = false;
}
@Override
protected ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
if (onlyFans)
{
return single ? net.minecraft.commands.arguments.EntityArgument.player() : net.minecraft.commands.arguments.EntityArgument.players();
}
return single ? net.minecraft.commands.arguments.EntityArgument.entity() : net.minecraft.commands.arguments.EntityArgument.entities();
}
@Override
protected Value getValueFromContext(CommandContext<CommandSourceStack> context, String param) throws CommandSyntaxException
{
Collection<? extends Entity> founds = net.minecraft.commands.arguments.EntityArgument.getOptionalEntities(context, param);
if (!single)
{
return ListValue.wrap(founds.stream().map(EntityValue::new));
}
if (founds.isEmpty())
{
return Value.NULL;
}
if (founds.size() == 1)
{
return new EntityValue(founds.iterator().next());
}
throw new SimpleCommandExceptionType(Component.literal("Multiple entities returned while only one was requested" + " for custom type " + suffix)).create();
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
onlyFans = config.getOrDefault("players", Value.FALSE).getBoolean();
single = config.getOrDefault("single", Value.FALSE).getBoolean();
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return EntityArgument::new;
}
}
private static class PlayerProfileArgument extends CommandArgument
{
boolean single;
private PlayerProfileArgument()
{
super("players", GameProfileArgument.gameProfile().getExamples(), false);
single = false;
}
@Override
protected ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return GameProfileArgument.gameProfile();
}
@Override
protected Value getValueFromContext(CommandContext<CommandSourceStack> context, String param) throws CommandSyntaxException
{
Collection<NameAndId> profiles = GameProfileArgument.getGameProfiles(context, param);
if (!single)
{
return ListValue.wrap(profiles.stream().map(p -> StringValue.of(p.name())));
}
int size = profiles.size();
if (size == 0)
{
return Value.NULL;
}
if (size == 1)
{
return StringValue.of(profiles.iterator().next().name());
}
throw new SimpleCommandExceptionType(Component.literal("Multiple game profiles returned while only one was requested" + " for custom type " + suffix)).create();
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
single = config.getOrDefault("single", Value.FALSE).getBoolean();
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return PlayerProfileArgument::new;
}
}
private static class ScoreholderArgument extends CommandArgument
{
boolean single;
private ScoreholderArgument()
{
super("scoreholder", ScoreHolderArgument.scoreHolder().getExamples(), false);
single = false;
suggestionProvider = param -> ScoreHolderArgument.SUGGEST_SCORE_HOLDERS;
}
@Override
protected ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return single ? ScoreHolderArgument.scoreHolder() : ScoreHolderArgument.scoreHolders();
}
@Override
protected Value getValueFromContext(CommandContext<CommandSourceStack> context, String param) throws CommandSyntaxException
{
Collection<ScoreHolder> holders = ScoreHolderArgument.getNames(context, param);
if (!single)
{
return ListValue.wrap(holders.stream().map(ValueConversions::of));
}
int size = holders.size();
if (size == 0)
{
return Value.NULL;
}
if (size == 1)
{
return ValueConversions.of(holders.iterator().next());
}
throw new SimpleCommandExceptionType(Component.literal("Multiple score holders returned while only one was requested" + " for custom type " + suffix)).create();
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
single = config.getOrDefault("single", Value.FALSE).getBoolean();
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return PlayerProfileArgument::new;
}
}
private static class TagArgument extends CommandArgument
{
boolean mapRequired;
private TagArgument()
{
super("tag", CompoundTagArgument.compoundTag().getExamples(), false);
mapRequired = true;
}
@Override
protected ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return mapRequired ? CompoundTagArgument.compoundTag() : NbtTagArgument.nbtTag();
}
@Override
protected Value getValueFromContext(CommandContext<CommandSourceStack> context, String param)
{
return mapRequired
? new NBTSerializableValue(CompoundTagArgument.getCompoundTag(context, param))
: new NBTSerializableValue(NbtTagArgument.getNbtTag(context, param));
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
mapRequired = !config.getOrDefault("allow_element", Value.FALSE).getBoolean();
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return TagArgument::new;
}
}
private static class CustomIdentifierArgument extends CommandArgument
{
Set<Identifier> validOptions = Collections.emptySet();
protected CustomIdentifierArgument()
{
super("identifier", Collections.emptyList(), true);
}
@Override
protected ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return IdentifierArgument.id();
}
@Override
protected Value getValueFromContext(CommandContext<CommandSourceStack> context, String param) throws CommandSyntaxException
{
Identifier choseValue = IdentifierArgument.getId(context, param);
if (!validOptions.isEmpty() && !validOptions.contains(choseValue))
{
throw new SimpleCommandExceptionType(Component.literal("Incorrect value for " + param + ": " + choseValue + " for custom type " + suffix)).create();
}
return ValueConversions.of(choseValue);
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return CustomIdentifierArgument::new;
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
if (config.containsKey("options"))
{
Value optionsValue = config.get("options");
if (!(optionsValue instanceof ListValue))
{
throw error("Custom sting type requires options passed as a list" + " for custom type " + suffix);
}
validOptions = ((ListValue) optionsValue).getItems().stream().map(v -> Identifier.parse(v.getString())).collect(Collectors.toSet());
}
}
}
private static class FloatArgument extends CommandArgument
{
private Double min = null;
private Double max = null;
private FloatArgument()
{
super("float", DoubleArgumentType.doubleArg().getExamples(), true);
}
@Override
public ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
if (min != null)
{
if (max != null)
{
return DoubleArgumentType.doubleArg(min, max);
}
return DoubleArgumentType.doubleArg(min);
}
return DoubleArgumentType.doubleArg();
}
@Override
public Value getValueFromContext(CommandContext<CommandSourceStack> context, String param)
{
return new NumericValue(DoubleArgumentType.getDouble(context, param));
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
if (config.containsKey("min"))
{
min = NumericValue.asNumber(config.get("min"), "min").getDouble();
}
if (config.containsKey("max"))
{
max = NumericValue.asNumber(config.get("max"), "max").getDouble();
}
if (max != null && min == null)
{
throw error("Double types cannot be only upper-bounded" + " for custom type " + suffix);
}
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return FloatArgument::new;
}
}
private static class IntArgument extends CommandArgument
{
private Long min = null;
private Long max = null;
private IntArgument()
{
super("int", LongArgumentType.longArg().getExamples(), true);
}
@Override
public ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
if (min != null)
{
if (max != null)
{
return LongArgumentType.longArg(min, max);
}
return LongArgumentType.longArg(min);
}
return LongArgumentType.longArg();
}
@Override
public Value getValueFromContext(CommandContext<CommandSourceStack> context, String param)
{
return new NumericValue(LongArgumentType.getLong(context, param));
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
if (config.containsKey("min"))
{
min = NumericValue.asNumber(config.get("min"), "min").getLong();
}
if (config.containsKey("max"))
{
max = NumericValue.asNumber(config.get("max"), "max").getLong();
}
if (max != null && min == null)
{
throw error("Double types cannot be only upper-bounded" + " for custom type " + suffix);
}
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return IntArgument::new;
}
}
private static class SlotArgument extends CommandArgument
{
private record ContainerIds(IntSet numericalIds, Set<String> commandIds)
{
}
private String restrict;
private static final Map<String, ContainerIds> RESTRICTED_CONTAINERS = new HashMap<>()
{{
int i;
for (String source : Arrays.asList("player", "enderchest", "equipment", "armor", "weapon", "container", "villager", "horse"))
{
put(source, new ContainerIds(new IntOpenHashSet(), new HashSet<>()));
}
for (i = 0; i < 41; i++)
{
get("player").numericalIds().add(i);
}
for (i = 0; i < 41; i++)
{
get("player").commandIds().add("container." + i);
}
for (i = 0; i < 9; i++)
{
get("player").commandIds().add("hotbar." + i);
}
for (i = 0; i < 27; i++)
{
get("player").commandIds().add("inventory." + i);
}
for (String place : Arrays.asList("weapon", "weapon.mainhand", "weapon.offhand"))
{
get("player").commandIds().add(place);
get("equipment").commandIds().add(place);
get("weapon").commandIds().add(place);
}
for (String place : Arrays.asList("armor.feet", "armor.legs", "armor.chest", "armor.head"))
{
get("player").commandIds().add(place);
get("equipment").commandIds().add(place);
get("armor").commandIds().add(place);
}
for (i = 0; i < 27; i++)
{
get("enderchest").numericalIds().add(200 + i);
}
for (i = 0; i < 27; i++)
{
get("enderchest").commandIds().add("enderchest." + i);
}
for (i = 0; i < 6; i++)
{
get("equipment").numericalIds().add(98 + i);
}
for (i = 0; i < 4; i++)
{
get("armor").numericalIds().add(100 + i);
}
for (i = 0; i < 2; i++)
{
get("weapon").numericalIds().add(98 + i);
}
for (i = 0; i < 54; i++)
{
get("container").numericalIds().add(i);
}
for (i = 0; i < 41; i++)
{
get("container").commandIds().add("container." + i);
}
for (i = 0; i < 8; i++)
{
get("villager").numericalIds().add(i);
}
for (i = 0; i < 8; i++)
{
get("villager").commandIds().add("villager." + i);
}
for (i = 0; i < 15; i++)
{
get("horse").numericalIds().add(500 + i);
}
for (i = 0; i < 15; i++)
{
get("horse").commandIds().add("horse." + i);
}
get("horse").numericalIds().add(400);
get("horse").commandIds().add("horse.saddle");
get("horse").numericalIds().add(401);
get("horse").commandIds().add("horse.armor");
}};
protected SlotArgument()
{
super("slot", net.minecraft.commands.arguments.SlotArgument.slot().getExamples(), false);
}
@Override
protected ArgumentType<?> getArgumentType(CarpetScriptHost host)
{
return net.minecraft.commands.arguments.SlotArgument.slot();
}
@Override
protected Value getValueFromContext(CommandContext<CommandSourceStack> context, String param) throws CommandSyntaxException
{
int slot = net.minecraft.commands.arguments.SlotArgument.getSlot(context, param);
if (restrict != null && !RESTRICTED_CONTAINERS.get(restrict).numericalIds().contains(slot))
{
throw new SimpleCommandExceptionType(Component.literal("Incorrect slot restricted to " + restrict + " for custom type " + suffix)).create();
}
return ValueConversions.ofVanillaSlotResult(slot);
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host) throws CommandSyntaxException
{
super.configure(config, host);
if (config.containsKey("restrict"))
{
restrict = config.get("restrict").getString().toLowerCase(Locale.ROOT);
needsMatching = true;
if (!RESTRICTED_CONTAINERS.containsKey(restrict))
{
throw error("Incorrect slot restriction " + restrict + " for custom type " + suffix);
}
}
}
@Override
protected Collection<String> getOptions(CommandContext<CommandSourceStack> context, CarpetScriptHost host) throws CommandSyntaxException
{
return restrict == null ? super.getOptions(context, host) : RESTRICTED_CONTAINERS.get(restrict).commandIds();
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return SlotArgument::new;
}
}
@FunctionalInterface
private interface ValueExtractor
{
Value apply(CommandContext<CommandSourceStack> ctx, String param) throws CommandSyntaxException;
}
@FunctionalInterface
private interface ArgumentProvider
{
ArgumentType<?> get() throws CommandSyntaxException;
}
@FunctionalInterface
private interface ArgumentProviderEx
{
ArgumentType<?> get(CommandBuildContext regAccess) throws CommandSyntaxException;
}
public static class VanillaUnconfigurableArgument extends CommandArgument
{
private final ArgumentProvider argumentTypeSupplier;
private final ArgumentProviderEx argumentTypeSupplierEx;
private final ValueExtractor valueExtractor;
private final boolean providesExamples;
public VanillaUnconfigurableArgument(
String suffix,
ArgumentProvider argumentTypeSupplier,
ValueExtractor valueExtractor,
boolean suggestFromExamples
)
{
super(suffix, null, suggestFromExamples);
try
{
this.examples = argumentTypeSupplier.get().getExamples();
}
catch (CommandSyntaxException e)
{
this.examples = Collections.emptyList();
}
this.providesExamples = suggestFromExamples;
this.argumentTypeSupplier = argumentTypeSupplier;
this.valueExtractor = valueExtractor;
this.argumentTypeSupplierEx = null;
}
public VanillaUnconfigurableArgument(
String suffix,
ArgumentProvider argumentTypeSupplier,
ValueExtractor valueExtractor,
SuggestionProvider<CommandSourceStack> suggester
)
{
super(suffix, Collections.emptyList(), false);
this.suggestionProvider = param -> suggester;
this.providesExamples = false;
this.argumentTypeSupplier = argumentTypeSupplier;
this.valueExtractor = valueExtractor;
this.argumentTypeSupplierEx = null;
}
public VanillaUnconfigurableArgument(
String suffix,
ArgumentProviderEx argumentTypeSupplier,
ValueExtractor valueExtractor,
boolean suggestFromExamples,
MinecraftServer server)
{
super(suffix, null, suggestFromExamples);
try
{
CommandBuildContext context = CommandBuildContext.simple(server.registryAccess(), server.getWorldData().enabledFeatures());
this.examples = argumentTypeSupplier.get(context).getExamples();
}
catch (CommandSyntaxException e)
{
this.examples = Collections.emptyList();
}
this.providesExamples = suggestFromExamples;
this.argumentTypeSupplierEx = argumentTypeSupplier;
this.valueExtractor = valueExtractor;
this.argumentTypeSupplier = null;
}
public VanillaUnconfigurableArgument(
String suffix,
ArgumentProviderEx argumentTypeSupplier,
ValueExtractor valueExtractor,
SuggestionProvider<CommandSourceStack> suggester
)
{
super(suffix, Collections.emptyList(), false);
this.suggestionProvider = param -> suggester;
this.providesExamples = false;
this.argumentTypeSupplierEx = argumentTypeSupplier;
this.valueExtractor = valueExtractor;
this.argumentTypeSupplier = null;
}
public VanillaUnconfigurableArgument(
String suffix,
ArgumentProviderEx argumentTypeSupplier,
ValueExtractor valueExtractor,
Function<String, SuggestionProvider<CommandSourceStack>> suggesterGen
)
{
super(suffix, Collections.emptyList(), false);
this.suggestionProvider = suggesterGen;
this.providesExamples = false;
this.argumentTypeSupplierEx = argumentTypeSupplier;
this.valueExtractor = valueExtractor;
this.argumentTypeSupplier = null;
}
public <T> VanillaUnconfigurableArgument(
String suffix,
ResourceKey<Registry<T>> registry
)
{
this(
suffix,
c -> ResourceArgument.resource(c, registry),
(c, p) -> ValueConversions.of(ResourceArgument.getResource(c, p, registry).key()),
(c, b) -> SharedSuggestionProvider.suggestResource(c.getSource().getServer().registryAccess().lookupOrThrow(registry).keySet(), b)
);
}
@Override
protected ArgumentType<?> getArgumentType(CarpetScriptHost host) throws CommandSyntaxException
{
return argumentTypeSupplier != null
? argumentTypeSupplier.get()
: argumentTypeSupplierEx.get(CommandBuildContext.simple(host.scriptServer().server.registryAccess(), host.scriptServer().server.getWorldData().enabledFeatures()));
}
@Override
protected Value getValueFromContext(CommandContext<CommandSourceStack> context, String param) throws CommandSyntaxException
{
return valueExtractor.apply(context, param);
}
@Override
protected Supplier<CommandArgument> factory(MinecraftServer server)
{
return argumentTypeSupplier != null
? (() -> new VanillaUnconfigurableArgument(getTypeSuffix(), argumentTypeSupplier, valueExtractor, providesExamples))
: (() -> new VanillaUnconfigurableArgument(getTypeSuffix(), argumentTypeSupplierEx, valueExtractor, providesExamples, server));
}
}
}