package com.xfestudio.xfeservermanager.api.command;
import com.xfestudio.xfeservermanager.api.util.ApiChecks;
import java.util.Set;
/** Semantic facts extracted from one parsed selector argument. */
public record SelectorFact(
String argumentName,
String rawExpression,
SelectorType selectorType,
Integer limit,
DistanceRange distance,
SelectorSort sort,
Set<String> includedEntityTypes,
Set<String> excludedEntityTypes,
Set<String> tags,
boolean hasNbtFilter,
boolean hasPredicateFilter,
int resolvedTargetCount,
Set<String> resolvedEntityTypes
) {
public static final int UNKNOWN_TARGET_COUNT = -1;
public SelectorFact {
argumentName = ApiChecks.notBlank(argumentName, "argumentName");
rawExpression = ApiChecks.notBlank(rawExpression, "rawExpression");
if (selectorType == null) {
throw new NullPointerException("selectorType");
}
if (limit != null && limit < 1) {
throw new IllegalArgumentException("limit must be positive when present");
}
if (sort == null) {
throw new NullPointerException("sort");
}
includedEntityTypes = ApiChecks.immutableLowercaseSet(includedEntityTypes, "includedEntityTypes");
excludedEntityTypes = ApiChecks.immutableLowercaseSet(excludedEntityTypes, "excludedEntityTypes");
tags = ApiChecks.immutableLowercaseSet(tags, "tags");
if (resolvedTargetCount < UNKNOWN_TARGET_COUNT) {
throw new IllegalArgumentException("resolvedTargetCount must be -1 or non-negative");
}
resolvedEntityTypes = ApiChecks.immutableLowercaseSet(resolvedEntityTypes, "resolvedEntityTypes");
}
public boolean hasExplicitEntityTypeFilter() {
return !includedEntityTypes.isEmpty() || !excludedEntityTypes.isEmpty();
}
public Set<String> effectiveEntityTypes() {
if (!resolvedEntityTypes.isEmpty()) {
return resolvedEntityTypes;
}
if (!includedEntityTypes.isEmpty()) {
// A type tag is a set, not a concrete registry id. Without platform-side
// expansion it must remain unknown so a deny-list cannot be bypassed by
// spelling the same entity through #namespace:tag.
return includedEntityTypes.stream()
.filter(value -> !value.startsWith("#"))
.collect(java.util.stream.Collectors.toUnmodifiableSet());
}
if (selectorType == SelectorType.ALL_PLAYERS
|| selectorType == SelectorType.NEAREST_PLAYER
|| selectorType == SelectorType.RANDOM_PLAYER) {
return Set.of("minecraft:player");
}
return Set.of();
}
public boolean resolutionSucceeded() {
return resolvedTargetCount != UNKNOWN_TARGET_COUNT;
}
}
package com.xfestudio.xfeservermanager.api.command;
import com.xfestudio.xfeservermanager.api.util.ApiChecks;
import java.util.Set;
/** Semantic facts extracted from one parsed selector argument. */
public record SelectorFact(
String argumentName,
String rawExpression,
SelectorType selectorType,
Integer limit,
DistanceRange distance,
SelectorSort sort,
Set<String> includedEntityTypes,
Set<String> excludedEntityTypes,
Set<String> tags,
boolean hasNbtFilter,
boolean hasPredicateFilter,
int resolvedTargetCount,
Set<String> resolvedEntityTypes
) {
public static final int UNKNOWN_TARGET_COUNT = -1;
public SelectorFact {
argumentName = ApiChecks.notBlank(argumentName, "argumentName");
rawExpression = ApiChecks.notBlank(rawExpression, "rawExpression");
if (selectorType == null) {
throw new NullPointerException("selectorType");
}
if (limit != null && limit < 1) {
throw new IllegalArgumentException("limit must be positive when present");
}
if (sort == null) {
throw new NullPointerException("sort");
}
includedEntityTypes = ApiChecks.immutableLowercaseSet(includedEntityTypes, "includedEntityTypes");
excludedEntityTypes = ApiChecks.immutableLowercaseSet(excludedEntityTypes, "excludedEntityTypes");
tags = ApiChecks.immutableLowercaseSet(tags, "tags");
if (resolvedTargetCount < UNKNOWN_TARGET_COUNT) {
throw new IllegalArgumentException("resolvedTargetCount must be -1 or non-negative");
}
resolvedEntityTypes = ApiChecks.immutableLowercaseSet(resolvedEntityTypes, "resolvedEntityTypes");
}
public boolean hasExplicitEntityTypeFilter() {
return !includedEntityTypes.isEmpty() || !excludedEntityTypes.isEmpty();
}
public Set<String> effectiveEntityTypes() {
if (!resolvedEntityTypes.isEmpty()) {
return resolvedEntityTypes;
}
if (!includedEntityTypes.isEmpty()) {
// A type tag is a set, not a concrete registry id. Without platform-side
// expansion it must remain unknown so a deny-list cannot be bypassed by
// spelling the same entity through #namespace:tag.
return includedEntityTypes.stream()
.filter(value -> !value.startsWith("#"))
.collect(java.util.stream.Collectors.toUnmodifiableSet());
}
if (selectorType == SelectorType.ALL_PLAYERS
|| selectorType == SelectorType.NEAREST_PLAYER
|| selectorType == SelectorType.RANDOM_PLAYER) {
return Set.of("minecraft:player");
}
return Set.of();
}
public boolean resolutionSucceeded() {
return resolvedTargetCount != UNKNOWN_TARGET_COUNT;
}
}