532 lines
25 KiB
Java
532 lines
25 KiB
Java
package net.minecraft.commands.arguments.selector.options;
|
|
|
|
import com.google.common.collect.Maps;
|
|
import com.mojang.brigadier.StringReader;
|
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
|
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
|
import java.util.Arrays;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.Map.Entry;
|
|
import java.util.function.Predicate;
|
|
import net.minecraft.advancements.AdvancementHolder;
|
|
import net.minecraft.advancements.AdvancementProgress;
|
|
import net.minecraft.advancements.CriterionProgress;
|
|
import net.minecraft.advancements.critereon.MinMaxBounds;
|
|
import net.minecraft.advancements.critereon.WrappedMinMaxBounds;
|
|
import net.minecraft.commands.SharedSuggestionProvider;
|
|
import net.minecraft.commands.arguments.selector.EntitySelector;
|
|
import net.minecraft.commands.arguments.selector.EntitySelectorParser;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.registries.BuiltInRegistries;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.NbtUtils;
|
|
import net.minecraft.nbt.TagParser;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.server.PlayerAdvancements;
|
|
import net.minecraft.server.ServerAdvancementManager;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.tags.TagKey;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.GameType;
|
|
import net.minecraft.world.level.storage.loot.LootContext;
|
|
import net.minecraft.world.level.storage.loot.LootParams;
|
|
import net.minecraft.world.level.storage.loot.LootParams.Builder;
|
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
|
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
|
import net.minecraft.world.scores.Objective;
|
|
import net.minecraft.world.scores.ReadOnlyScoreInfo;
|
|
import net.minecraft.world.scores.Scoreboard;
|
|
import net.minecraft.world.scores.Team;
|
|
|
|
public class EntitySelectorOptions {
|
|
private static final Map<String, EntitySelectorOptions.Option> OPTIONS = Maps.<String, EntitySelectorOptions.Option>newHashMap();
|
|
public static final DynamicCommandExceptionType ERROR_UNKNOWN_OPTION = new DynamicCommandExceptionType(
|
|
object -> Component.translatableEscape("argument.entity.options.unknown", object)
|
|
);
|
|
public static final DynamicCommandExceptionType ERROR_INAPPLICABLE_OPTION = new DynamicCommandExceptionType(
|
|
object -> Component.translatableEscape("argument.entity.options.inapplicable", object)
|
|
);
|
|
public static final SimpleCommandExceptionType ERROR_RANGE_NEGATIVE = new SimpleCommandExceptionType(
|
|
Component.translatable("argument.entity.options.distance.negative")
|
|
);
|
|
public static final SimpleCommandExceptionType ERROR_LEVEL_NEGATIVE = new SimpleCommandExceptionType(
|
|
Component.translatable("argument.entity.options.level.negative")
|
|
);
|
|
public static final SimpleCommandExceptionType ERROR_LIMIT_TOO_SMALL = new SimpleCommandExceptionType(
|
|
Component.translatable("argument.entity.options.limit.toosmall")
|
|
);
|
|
public static final DynamicCommandExceptionType ERROR_SORT_UNKNOWN = new DynamicCommandExceptionType(
|
|
object -> Component.translatableEscape("argument.entity.options.sort.irreversible", object)
|
|
);
|
|
public static final DynamicCommandExceptionType ERROR_GAME_MODE_INVALID = new DynamicCommandExceptionType(
|
|
object -> Component.translatableEscape("argument.entity.options.mode.invalid", object)
|
|
);
|
|
public static final DynamicCommandExceptionType ERROR_ENTITY_TYPE_INVALID = new DynamicCommandExceptionType(
|
|
object -> Component.translatableEscape("argument.entity.options.type.invalid", object)
|
|
);
|
|
|
|
private static void register(String id, EntitySelectorOptions.Modifier handler, Predicate<EntitySelectorParser> predicate, Component tooltip) {
|
|
OPTIONS.put(id, new EntitySelectorOptions.Option(handler, predicate, tooltip));
|
|
}
|
|
|
|
public static void bootStrap() {
|
|
if (OPTIONS.isEmpty()) {
|
|
register("name", entitySelectorParser -> {
|
|
int i = entitySelectorParser.getReader().getCursor();
|
|
boolean bl = entitySelectorParser.shouldInvertValue();
|
|
String string = entitySelectorParser.getReader().readString();
|
|
if (entitySelectorParser.hasNameNotEquals() && !bl) {
|
|
entitySelectorParser.getReader().setCursor(i);
|
|
throw ERROR_INAPPLICABLE_OPTION.createWithContext(entitySelectorParser.getReader(), "name");
|
|
} else {
|
|
if (bl) {
|
|
entitySelectorParser.setHasNameNotEquals(true);
|
|
} else {
|
|
entitySelectorParser.setHasNameEquals(true);
|
|
}
|
|
|
|
entitySelectorParser.addPredicate(entity -> entity.getName().getString().equals(string) != bl);
|
|
}
|
|
}, entitySelectorParser -> !entitySelectorParser.hasNameEquals(), Component.translatable("argument.entity.options.name.description"));
|
|
register("distance", entitySelectorParser -> {
|
|
int i = entitySelectorParser.getReader().getCursor();
|
|
MinMaxBounds.Doubles doubles = MinMaxBounds.Doubles.fromReader(entitySelectorParser.getReader());
|
|
if ((!doubles.min().isPresent() || !((Double)doubles.min().get() < 0.0)) && (!doubles.max().isPresent() || !((Double)doubles.max().get() < 0.0))) {
|
|
entitySelectorParser.setDistance(doubles);
|
|
entitySelectorParser.setWorldLimited();
|
|
} else {
|
|
entitySelectorParser.getReader().setCursor(i);
|
|
throw ERROR_RANGE_NEGATIVE.createWithContext(entitySelectorParser.getReader());
|
|
}
|
|
}, entitySelectorParser -> entitySelectorParser.getDistance().isAny(), Component.translatable("argument.entity.options.distance.description"));
|
|
register("level", entitySelectorParser -> {
|
|
int i = entitySelectorParser.getReader().getCursor();
|
|
MinMaxBounds.Ints ints = MinMaxBounds.Ints.fromReader(entitySelectorParser.getReader());
|
|
if ((!ints.min().isPresent() || (Integer)ints.min().get() >= 0) && (!ints.max().isPresent() || (Integer)ints.max().get() >= 0)) {
|
|
entitySelectorParser.setLevel(ints);
|
|
entitySelectorParser.setIncludesEntities(false);
|
|
} else {
|
|
entitySelectorParser.getReader().setCursor(i);
|
|
throw ERROR_LEVEL_NEGATIVE.createWithContext(entitySelectorParser.getReader());
|
|
}
|
|
}, entitySelectorParser -> entitySelectorParser.getLevel().isAny(), Component.translatable("argument.entity.options.level.description"));
|
|
register("x", entitySelectorParser -> {
|
|
entitySelectorParser.setWorldLimited();
|
|
entitySelectorParser.setX(entitySelectorParser.getReader().readDouble());
|
|
}, entitySelectorParser -> entitySelectorParser.getX() == null, Component.translatable("argument.entity.options.x.description"));
|
|
register("y", entitySelectorParser -> {
|
|
entitySelectorParser.setWorldLimited();
|
|
entitySelectorParser.setY(entitySelectorParser.getReader().readDouble());
|
|
}, entitySelectorParser -> entitySelectorParser.getY() == null, Component.translatable("argument.entity.options.y.description"));
|
|
register("z", entitySelectorParser -> {
|
|
entitySelectorParser.setWorldLimited();
|
|
entitySelectorParser.setZ(entitySelectorParser.getReader().readDouble());
|
|
}, entitySelectorParser -> entitySelectorParser.getZ() == null, Component.translatable("argument.entity.options.z.description"));
|
|
register("dx", entitySelectorParser -> {
|
|
entitySelectorParser.setWorldLimited();
|
|
entitySelectorParser.setDeltaX(entitySelectorParser.getReader().readDouble());
|
|
}, entitySelectorParser -> entitySelectorParser.getDeltaX() == null, Component.translatable("argument.entity.options.dx.description"));
|
|
register("dy", entitySelectorParser -> {
|
|
entitySelectorParser.setWorldLimited();
|
|
entitySelectorParser.setDeltaY(entitySelectorParser.getReader().readDouble());
|
|
}, entitySelectorParser -> entitySelectorParser.getDeltaY() == null, Component.translatable("argument.entity.options.dy.description"));
|
|
register("dz", entitySelectorParser -> {
|
|
entitySelectorParser.setWorldLimited();
|
|
entitySelectorParser.setDeltaZ(entitySelectorParser.getReader().readDouble());
|
|
}, entitySelectorParser -> entitySelectorParser.getDeltaZ() == null, Component.translatable("argument.entity.options.dz.description"));
|
|
register(
|
|
"x_rotation",
|
|
entitySelectorParser -> entitySelectorParser.setRotX(WrappedMinMaxBounds.fromReader(entitySelectorParser.getReader(), true, Mth::wrapDegrees)),
|
|
entitySelectorParser -> entitySelectorParser.getRotX() == WrappedMinMaxBounds.ANY,
|
|
Component.translatable("argument.entity.options.x_rotation.description")
|
|
);
|
|
register(
|
|
"y_rotation",
|
|
entitySelectorParser -> entitySelectorParser.setRotY(WrappedMinMaxBounds.fromReader(entitySelectorParser.getReader(), true, Mth::wrapDegrees)),
|
|
entitySelectorParser -> entitySelectorParser.getRotY() == WrappedMinMaxBounds.ANY,
|
|
Component.translatable("argument.entity.options.y_rotation.description")
|
|
);
|
|
register(
|
|
"limit",
|
|
entitySelectorParser -> {
|
|
int i = entitySelectorParser.getReader().getCursor();
|
|
int j = entitySelectorParser.getReader().readInt();
|
|
if (j < 1) {
|
|
entitySelectorParser.getReader().setCursor(i);
|
|
throw ERROR_LIMIT_TOO_SMALL.createWithContext(entitySelectorParser.getReader());
|
|
} else {
|
|
entitySelectorParser.setMaxResults(j);
|
|
entitySelectorParser.setLimited(true);
|
|
}
|
|
},
|
|
entitySelectorParser -> !entitySelectorParser.isCurrentEntity() && !entitySelectorParser.isLimited(),
|
|
Component.translatable("argument.entity.options.limit.description")
|
|
);
|
|
register(
|
|
"sort",
|
|
entitySelectorParser -> {
|
|
int i = entitySelectorParser.getReader().getCursor();
|
|
String string = entitySelectorParser.getReader().readUnquotedString();
|
|
entitySelectorParser.setSuggestions(
|
|
(suggestionsBuilder, consumer) -> SharedSuggestionProvider.suggest(Arrays.asList("nearest", "furthest", "random", "arbitrary"), suggestionsBuilder)
|
|
);
|
|
|
|
entitySelectorParser.setOrder(switch (string) {
|
|
case "nearest" -> EntitySelectorParser.ORDER_NEAREST;
|
|
case "furthest" -> EntitySelectorParser.ORDER_FURTHEST;
|
|
case "random" -> EntitySelectorParser.ORDER_RANDOM;
|
|
case "arbitrary" -> EntitySelector.ORDER_ARBITRARY;
|
|
default -> {
|
|
entitySelectorParser.getReader().setCursor(i);
|
|
throw ERROR_SORT_UNKNOWN.createWithContext(entitySelectorParser.getReader(), string);
|
|
}
|
|
});
|
|
entitySelectorParser.setSorted(true);
|
|
},
|
|
entitySelectorParser -> !entitySelectorParser.isCurrentEntity() && !entitySelectorParser.isSorted(),
|
|
Component.translatable("argument.entity.options.sort.description")
|
|
);
|
|
register("gamemode", entitySelectorParser -> {
|
|
entitySelectorParser.setSuggestions((suggestionsBuilder, consumer) -> {
|
|
String stringx = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT);
|
|
boolean blx = !entitySelectorParser.hasGamemodeNotEquals();
|
|
boolean bl2 = true;
|
|
if (!stringx.isEmpty()) {
|
|
if (stringx.charAt(0) == '!') {
|
|
blx = false;
|
|
stringx = stringx.substring(1);
|
|
} else {
|
|
bl2 = false;
|
|
}
|
|
}
|
|
|
|
for (GameType gameTypex : GameType.values()) {
|
|
if (gameTypex.getName().toLowerCase(Locale.ROOT).startsWith(stringx)) {
|
|
if (bl2) {
|
|
suggestionsBuilder.suggest("!" + gameTypex.getName());
|
|
}
|
|
|
|
if (blx) {
|
|
suggestionsBuilder.suggest(gameTypex.getName());
|
|
}
|
|
}
|
|
}
|
|
|
|
return suggestionsBuilder.buildFuture();
|
|
});
|
|
int i = entitySelectorParser.getReader().getCursor();
|
|
boolean bl = entitySelectorParser.shouldInvertValue();
|
|
if (entitySelectorParser.hasGamemodeNotEquals() && !bl) {
|
|
entitySelectorParser.getReader().setCursor(i);
|
|
throw ERROR_INAPPLICABLE_OPTION.createWithContext(entitySelectorParser.getReader(), "gamemode");
|
|
} else {
|
|
String string = entitySelectorParser.getReader().readUnquotedString();
|
|
GameType gameType = GameType.byName(string, null);
|
|
if (gameType == null) {
|
|
entitySelectorParser.getReader().setCursor(i);
|
|
throw ERROR_GAME_MODE_INVALID.createWithContext(entitySelectorParser.getReader(), string);
|
|
} else {
|
|
entitySelectorParser.setIncludesEntities(false);
|
|
entitySelectorParser.addPredicate(entity -> {
|
|
if (entity instanceof ServerPlayer serverPlayer) {
|
|
GameType gameType2 = serverPlayer.gameMode();
|
|
return gameType2 == gameType ^ bl;
|
|
} else {
|
|
return false;
|
|
}
|
|
});
|
|
if (bl) {
|
|
entitySelectorParser.setHasGamemodeNotEquals(true);
|
|
} else {
|
|
entitySelectorParser.setHasGamemodeEquals(true);
|
|
}
|
|
}
|
|
}
|
|
}, entitySelectorParser -> !entitySelectorParser.hasGamemodeEquals(), Component.translatable("argument.entity.options.gamemode.description"));
|
|
register("team", entitySelectorParser -> {
|
|
boolean bl = entitySelectorParser.shouldInvertValue();
|
|
String string = entitySelectorParser.getReader().readUnquotedString();
|
|
entitySelectorParser.addPredicate(entity -> {
|
|
Team team = entity.getTeam();
|
|
String string2 = team == null ? "" : team.getName();
|
|
return string2.equals(string) != bl;
|
|
});
|
|
if (bl) {
|
|
entitySelectorParser.setHasTeamNotEquals(true);
|
|
} else {
|
|
entitySelectorParser.setHasTeamEquals(true);
|
|
}
|
|
}, entitySelectorParser -> !entitySelectorParser.hasTeamEquals(), Component.translatable("argument.entity.options.team.description"));
|
|
register(
|
|
"type",
|
|
entitySelectorParser -> {
|
|
entitySelectorParser.setSuggestions(
|
|
(suggestionsBuilder, consumer) -> {
|
|
SharedSuggestionProvider.suggestResource(BuiltInRegistries.ENTITY_TYPE.keySet(), suggestionsBuilder, String.valueOf('!'));
|
|
SharedSuggestionProvider.suggestResource(BuiltInRegistries.ENTITY_TYPE.getTags().map(named -> named.key().location()), suggestionsBuilder, "!#");
|
|
if (!entitySelectorParser.isTypeLimitedInversely()) {
|
|
SharedSuggestionProvider.suggestResource(BuiltInRegistries.ENTITY_TYPE.keySet(), suggestionsBuilder);
|
|
SharedSuggestionProvider.suggestResource(
|
|
BuiltInRegistries.ENTITY_TYPE.getTags().map(named -> named.key().location()), suggestionsBuilder, String.valueOf('#')
|
|
);
|
|
}
|
|
|
|
return suggestionsBuilder.buildFuture();
|
|
}
|
|
);
|
|
int i = entitySelectorParser.getReader().getCursor();
|
|
boolean bl = entitySelectorParser.shouldInvertValue();
|
|
if (entitySelectorParser.isTypeLimitedInversely() && !bl) {
|
|
entitySelectorParser.getReader().setCursor(i);
|
|
throw ERROR_INAPPLICABLE_OPTION.createWithContext(entitySelectorParser.getReader(), "type");
|
|
} else {
|
|
if (bl) {
|
|
entitySelectorParser.setTypeLimitedInversely();
|
|
}
|
|
|
|
if (entitySelectorParser.isTag()) {
|
|
TagKey<EntityType<?>> tagKey = TagKey.create(Registries.ENTITY_TYPE, ResourceLocation.read(entitySelectorParser.getReader()));
|
|
entitySelectorParser.addPredicate(entity -> entity.getType().is(tagKey) != bl);
|
|
} else {
|
|
ResourceLocation resourceLocation = ResourceLocation.read(entitySelectorParser.getReader());
|
|
EntityType<?> entityType = (EntityType<?>)BuiltInRegistries.ENTITY_TYPE.getOptional(resourceLocation).orElseThrow(() -> {
|
|
entitySelectorParser.getReader().setCursor(i);
|
|
return ERROR_ENTITY_TYPE_INVALID.createWithContext(entitySelectorParser.getReader(), resourceLocation.toString());
|
|
});
|
|
if (Objects.equals(EntityType.PLAYER, entityType) && !bl) {
|
|
entitySelectorParser.setIncludesEntities(false);
|
|
}
|
|
|
|
entitySelectorParser.addPredicate(entity -> Objects.equals(entityType, entity.getType()) != bl);
|
|
if (!bl) {
|
|
entitySelectorParser.limitToType(entityType);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
entitySelectorParser -> !entitySelectorParser.isTypeLimited(),
|
|
Component.translatable("argument.entity.options.type.description")
|
|
);
|
|
register("tag", entitySelectorParser -> {
|
|
boolean bl = entitySelectorParser.shouldInvertValue();
|
|
String string = entitySelectorParser.getReader().readUnquotedString();
|
|
entitySelectorParser.addPredicate(entity -> "".equals(string) ? entity.getTags().isEmpty() != bl : entity.getTags().contains(string) != bl);
|
|
}, entitySelectorParser -> true, Component.translatable("argument.entity.options.tag.description"));
|
|
register("nbt", entitySelectorParser -> {
|
|
boolean bl = entitySelectorParser.shouldInvertValue();
|
|
CompoundTag compoundTag = TagParser.parseCompoundAsArgument(entitySelectorParser.getReader());
|
|
entitySelectorParser.addPredicate(entity -> {
|
|
CompoundTag compoundTag2 = entity.saveWithoutId(new CompoundTag());
|
|
if (entity instanceof ServerPlayer serverPlayer) {
|
|
ItemStack itemStack = serverPlayer.getInventory().getSelectedItem();
|
|
if (!itemStack.isEmpty()) {
|
|
compoundTag2.put("SelectedItem", itemStack.save(serverPlayer.registryAccess()));
|
|
}
|
|
}
|
|
|
|
return NbtUtils.compareNbt(compoundTag, compoundTag2, true) != bl;
|
|
});
|
|
}, entitySelectorParser -> true, Component.translatable("argument.entity.options.nbt.description"));
|
|
register("scores", entitySelectorParser -> {
|
|
StringReader stringReader = entitySelectorParser.getReader();
|
|
Map<String, MinMaxBounds.Ints> map = Maps.<String, MinMaxBounds.Ints>newHashMap();
|
|
stringReader.expect('{');
|
|
stringReader.skipWhitespace();
|
|
|
|
while (stringReader.canRead() && stringReader.peek() != '}') {
|
|
stringReader.skipWhitespace();
|
|
String string = stringReader.readUnquotedString();
|
|
stringReader.skipWhitespace();
|
|
stringReader.expect('=');
|
|
stringReader.skipWhitespace();
|
|
MinMaxBounds.Ints ints = MinMaxBounds.Ints.fromReader(stringReader);
|
|
map.put(string, ints);
|
|
stringReader.skipWhitespace();
|
|
if (stringReader.canRead() && stringReader.peek() == ',') {
|
|
stringReader.skip();
|
|
}
|
|
}
|
|
|
|
stringReader.expect('}');
|
|
if (!map.isEmpty()) {
|
|
entitySelectorParser.addPredicate(entity -> {
|
|
Scoreboard scoreboard = entity.getServer().getScoreboard();
|
|
|
|
for (Entry<String, MinMaxBounds.Ints> entry : map.entrySet()) {
|
|
Objective objective = scoreboard.getObjective((String)entry.getKey());
|
|
if (objective == null) {
|
|
return false;
|
|
}
|
|
|
|
ReadOnlyScoreInfo readOnlyScoreInfo = scoreboard.getPlayerScoreInfo(entity, objective);
|
|
if (readOnlyScoreInfo == null) {
|
|
return false;
|
|
}
|
|
|
|
if (!((MinMaxBounds.Ints)entry.getValue()).matches(readOnlyScoreInfo.value())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
entitySelectorParser.setHasScores(true);
|
|
}, entitySelectorParser -> !entitySelectorParser.hasScores(), Component.translatable("argument.entity.options.scores.description"));
|
|
register("advancements", entitySelectorParser -> {
|
|
StringReader stringReader = entitySelectorParser.getReader();
|
|
Map<ResourceLocation, Predicate<AdvancementProgress>> map = Maps.<ResourceLocation, Predicate<AdvancementProgress>>newHashMap();
|
|
stringReader.expect('{');
|
|
stringReader.skipWhitespace();
|
|
|
|
while (stringReader.canRead() && stringReader.peek() != '}') {
|
|
stringReader.skipWhitespace();
|
|
ResourceLocation resourceLocation = ResourceLocation.read(stringReader);
|
|
stringReader.skipWhitespace();
|
|
stringReader.expect('=');
|
|
stringReader.skipWhitespace();
|
|
if (stringReader.canRead() && stringReader.peek() == '{') {
|
|
Map<String, Predicate<CriterionProgress>> map2 = Maps.<String, Predicate<CriterionProgress>>newHashMap();
|
|
stringReader.skipWhitespace();
|
|
stringReader.expect('{');
|
|
stringReader.skipWhitespace();
|
|
|
|
while (stringReader.canRead() && stringReader.peek() != '}') {
|
|
stringReader.skipWhitespace();
|
|
String string = stringReader.readUnquotedString();
|
|
stringReader.skipWhitespace();
|
|
stringReader.expect('=');
|
|
stringReader.skipWhitespace();
|
|
boolean bl = stringReader.readBoolean();
|
|
map2.put(string, (Predicate)criterionProgress -> criterionProgress.isDone() == bl);
|
|
stringReader.skipWhitespace();
|
|
if (stringReader.canRead() && stringReader.peek() == ',') {
|
|
stringReader.skip();
|
|
}
|
|
}
|
|
|
|
stringReader.skipWhitespace();
|
|
stringReader.expect('}');
|
|
stringReader.skipWhitespace();
|
|
map.put(resourceLocation, (Predicate)advancementProgress -> {
|
|
for (Entry<String, Predicate<CriterionProgress>> entry : map2.entrySet()) {
|
|
CriterionProgress criterionProgress = advancementProgress.getCriterion((String)entry.getKey());
|
|
if (criterionProgress == null || !((Predicate)entry.getValue()).test(criterionProgress)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
} else {
|
|
boolean bl2 = stringReader.readBoolean();
|
|
map.put(resourceLocation, (Predicate)advancementProgress -> advancementProgress.isDone() == bl2);
|
|
}
|
|
|
|
stringReader.skipWhitespace();
|
|
if (stringReader.canRead() && stringReader.peek() == ',') {
|
|
stringReader.skip();
|
|
}
|
|
}
|
|
|
|
stringReader.expect('}');
|
|
if (!map.isEmpty()) {
|
|
entitySelectorParser.addPredicate(entity -> {
|
|
if (!(entity instanceof ServerPlayer serverPlayer)) {
|
|
return false;
|
|
} else {
|
|
PlayerAdvancements playerAdvancements = serverPlayer.getAdvancements();
|
|
ServerAdvancementManager serverAdvancementManager = serverPlayer.getServer().getAdvancements();
|
|
|
|
for (Entry<ResourceLocation, Predicate<AdvancementProgress>> entry : map.entrySet()) {
|
|
AdvancementHolder advancementHolder = serverAdvancementManager.get((ResourceLocation)entry.getKey());
|
|
if (advancementHolder == null || !((Predicate)entry.getValue()).test(playerAdvancements.getOrStartProgress(advancementHolder))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
});
|
|
entitySelectorParser.setIncludesEntities(false);
|
|
}
|
|
|
|
entitySelectorParser.setHasAdvancements(true);
|
|
}, entitySelectorParser -> !entitySelectorParser.hasAdvancements(), Component.translatable("argument.entity.options.advancements.description"));
|
|
register(
|
|
"predicate",
|
|
entitySelectorParser -> {
|
|
boolean bl = entitySelectorParser.shouldInvertValue();
|
|
ResourceKey<LootItemCondition> resourceKey = ResourceKey.create(Registries.PREDICATE, ResourceLocation.read(entitySelectorParser.getReader()));
|
|
entitySelectorParser.addPredicate(
|
|
entity -> {
|
|
if (!(entity.level() instanceof ServerLevel)) {
|
|
return false;
|
|
} else {
|
|
ServerLevel serverLevel = (ServerLevel)entity.level();
|
|
Optional<LootItemCondition> optional = serverLevel.getServer().reloadableRegistries().lookup().get(resourceKey).map(Holder::value);
|
|
if (optional.isEmpty()) {
|
|
return false;
|
|
} else {
|
|
LootParams lootParams = new Builder(serverLevel)
|
|
.withParameter(LootContextParams.THIS_ENTITY, entity)
|
|
.withParameter(LootContextParams.ORIGIN, entity.position())
|
|
.create(LootContextParamSets.SELECTOR);
|
|
LootContext lootContext = new LootContext.Builder(lootParams).create(Optional.empty());
|
|
lootContext.pushVisitedElement(LootContext.createVisitedEntry((LootItemCondition)optional.get()));
|
|
return bl ^ ((LootItemCondition)optional.get()).test(lootContext);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
},
|
|
entitySelectorParser -> true,
|
|
Component.translatable("argument.entity.options.predicate.description")
|
|
);
|
|
}
|
|
}
|
|
|
|
public static EntitySelectorOptions.Modifier get(EntitySelectorParser parser, String id, int cursor) throws CommandSyntaxException {
|
|
EntitySelectorOptions.Option option = (EntitySelectorOptions.Option)OPTIONS.get(id);
|
|
if (option != null) {
|
|
if (option.canUse.test(parser)) {
|
|
return option.modifier;
|
|
} else {
|
|
throw ERROR_INAPPLICABLE_OPTION.createWithContext(parser.getReader(), id);
|
|
}
|
|
} else {
|
|
parser.getReader().setCursor(cursor);
|
|
throw ERROR_UNKNOWN_OPTION.createWithContext(parser.getReader(), id);
|
|
}
|
|
}
|
|
|
|
public static void suggestNames(EntitySelectorParser parser, SuggestionsBuilder builder) {
|
|
String string = builder.getRemaining().toLowerCase(Locale.ROOT);
|
|
|
|
for (Entry<String, EntitySelectorOptions.Option> entry : OPTIONS.entrySet()) {
|
|
if (((EntitySelectorOptions.Option)entry.getValue()).canUse.test(parser) && ((String)entry.getKey()).toLowerCase(Locale.ROOT).startsWith(string)) {
|
|
builder.suggest((String)entry.getKey() + "=", ((EntitySelectorOptions.Option)entry.getValue()).description);
|
|
}
|
|
}
|
|
}
|
|
|
|
public interface Modifier {
|
|
void handle(EntitySelectorParser entitySelectorParser) throws CommandSyntaxException;
|
|
}
|
|
|
|
record Option(EntitySelectorOptions.Modifier modifier, Predicate<EntitySelectorParser> canUse, Component description) {
|
|
}
|
|
}
|