265 lines
11 KiB
Java
265 lines
11 KiB
Java
package net.minecraft.advancements.critereon;
|
|
|
|
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.datafixers.util.Either;
|
|
import com.mojang.serialization.Codec;
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
import io.netty.buffer.ByteBuf;
|
|
import java.util.Optional;
|
|
import java.util.function.Function;
|
|
import java.util.function.Supplier;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.codec.ByteBufCodecs;
|
|
import net.minecraft.network.codec.StreamCodec;
|
|
|
|
public interface MinMaxBounds<T extends Number> {
|
|
SimpleCommandExceptionType ERROR_EMPTY = new SimpleCommandExceptionType(Component.translatable("argument.range.empty"));
|
|
SimpleCommandExceptionType ERROR_SWAPPED = new SimpleCommandExceptionType(Component.translatable("argument.range.swapped"));
|
|
|
|
Optional<T> min();
|
|
|
|
Optional<T> max();
|
|
|
|
default boolean isAny() {
|
|
return this.min().isEmpty() && this.max().isEmpty();
|
|
}
|
|
|
|
default Optional<T> unwrapPoint() {
|
|
Optional<T> optional = this.min();
|
|
Optional<T> optional2 = this.max();
|
|
return optional.equals(optional2) ? optional : Optional.empty();
|
|
}
|
|
|
|
static <T extends Number, R extends MinMaxBounds<T>> Codec<R> createCodec(Codec<T> codec, MinMaxBounds.BoundsFactory<T, R> boundsFactory) {
|
|
Codec<R> codec2 = RecordCodecBuilder.create(
|
|
instance -> instance.group(codec.optionalFieldOf("min").forGetter(MinMaxBounds::min), codec.optionalFieldOf("max").forGetter(MinMaxBounds::max))
|
|
.apply(instance, boundsFactory::create)
|
|
);
|
|
return Codec.either(codec2, codec)
|
|
.xmap(either -> either.map(minMaxBounds -> minMaxBounds, number -> boundsFactory.create(Optional.of(number), Optional.of(number))), minMaxBounds -> {
|
|
Optional<T> optional = minMaxBounds.unwrapPoint();
|
|
return optional.isPresent() ? Either.right((Number)optional.get()) : Either.left(minMaxBounds);
|
|
});
|
|
}
|
|
|
|
static <B extends ByteBuf, T extends Number, R extends MinMaxBounds<T>> StreamCodec<B, R> createStreamCodec(
|
|
StreamCodec<B, T> streamCodec, MinMaxBounds.BoundsFactory<T, R> boundsFactory
|
|
) {
|
|
return new StreamCodec<B, R>() {
|
|
private static final int MIN_FLAG = 1;
|
|
public static final int MAX_FLAG = 2;
|
|
|
|
public R decode(B byteBuf) {
|
|
byte b = byteBuf.readByte();
|
|
Optional<T> optional = (b & 1) != 0 ? Optional.of(streamCodec.decode(byteBuf)) : Optional.empty();
|
|
Optional<T> optional2 = (b & 2) != 0 ? Optional.of(streamCodec.decode(byteBuf)) : Optional.empty();
|
|
return boundsFactory.create(optional, optional2);
|
|
}
|
|
|
|
public void encode(B byteBuf, R minMaxBounds) {
|
|
Optional<T> optional = minMaxBounds.min();
|
|
Optional<T> optional2 = minMaxBounds.max();
|
|
byteBuf.writeByte((optional.isPresent() ? 1 : 0) | (optional2.isPresent() ? 2 : 0));
|
|
optional.ifPresent(number -> streamCodec.encode(byteBuf, (T)number));
|
|
optional2.ifPresent(number -> streamCodec.encode(byteBuf, (T)number));
|
|
}
|
|
};
|
|
}
|
|
|
|
static <T extends Number, R extends MinMaxBounds<T>> R fromReader(
|
|
StringReader reader,
|
|
MinMaxBounds.BoundsFromReaderFactory<T, R> boundedFactory,
|
|
Function<String, T> valueFactory,
|
|
Supplier<DynamicCommandExceptionType> commandExceptionSupplier,
|
|
Function<T, T> formatter
|
|
) throws CommandSyntaxException {
|
|
if (!reader.canRead()) {
|
|
throw ERROR_EMPTY.createWithContext(reader);
|
|
} else {
|
|
int i = reader.getCursor();
|
|
|
|
try {
|
|
Optional<T> optional = readNumber(reader, valueFactory, commandExceptionSupplier).map(formatter);
|
|
Optional<T> optional2;
|
|
if (reader.canRead(2) && reader.peek() == '.' && reader.peek(1) == '.') {
|
|
reader.skip();
|
|
reader.skip();
|
|
optional2 = readNumber(reader, valueFactory, commandExceptionSupplier).map(formatter);
|
|
if (optional.isEmpty() && optional2.isEmpty()) {
|
|
throw ERROR_EMPTY.createWithContext(reader);
|
|
}
|
|
} else {
|
|
optional2 = optional;
|
|
}
|
|
|
|
if (optional.isEmpty() && optional2.isEmpty()) {
|
|
throw ERROR_EMPTY.createWithContext(reader);
|
|
} else {
|
|
return boundedFactory.create(reader, optional, optional2);
|
|
}
|
|
} catch (CommandSyntaxException var8) {
|
|
reader.setCursor(i);
|
|
throw new CommandSyntaxException(var8.getType(), var8.getRawMessage(), var8.getInput(), i);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static <T extends Number> Optional<T> readNumber(
|
|
StringReader reader, Function<String, T> stringToValueFunction, Supplier<DynamicCommandExceptionType> commandExceptionSupplier
|
|
) throws CommandSyntaxException {
|
|
int i = reader.getCursor();
|
|
|
|
while (reader.canRead() && isAllowedInputChat(reader)) {
|
|
reader.skip();
|
|
}
|
|
|
|
String string = reader.getString().substring(i, reader.getCursor());
|
|
if (string.isEmpty()) {
|
|
return Optional.empty();
|
|
} else {
|
|
try {
|
|
return Optional.of((Number)stringToValueFunction.apply(string));
|
|
} catch (NumberFormatException var6) {
|
|
throw ((DynamicCommandExceptionType)commandExceptionSupplier.get()).createWithContext(reader, string);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static boolean isAllowedInputChat(StringReader reader) {
|
|
char c = reader.peek();
|
|
if ((c < '0' || c > '9') && c != '-') {
|
|
return c != '.' ? false : !reader.canRead(2) || reader.peek(1) != '.';
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
@FunctionalInterface
|
|
public interface BoundsFactory<T extends Number, R extends MinMaxBounds<T>> {
|
|
R create(Optional<T> optional, Optional<T> optional2);
|
|
}
|
|
|
|
@FunctionalInterface
|
|
public interface BoundsFromReaderFactory<T extends Number, R extends MinMaxBounds<T>> {
|
|
R create(StringReader stringReader, Optional<T> optional, Optional<T> optional2) throws CommandSyntaxException;
|
|
}
|
|
|
|
public record Doubles(Optional<Double> min, Optional<Double> max, Optional<Double> minSq, Optional<Double> maxSq) implements MinMaxBounds<Double> {
|
|
public static final MinMaxBounds.Doubles ANY = new MinMaxBounds.Doubles(Optional.empty(), Optional.empty());
|
|
public static final Codec<MinMaxBounds.Doubles> CODEC = MinMaxBounds.createCodec((Codec<T>)Codec.DOUBLE, MinMaxBounds.Doubles::new);
|
|
public static final StreamCodec<ByteBuf, MinMaxBounds.Doubles> STREAM_CODEC = MinMaxBounds.createStreamCodec(
|
|
(StreamCodec<ByteBuf, T>)ByteBufCodecs.DOUBLE, MinMaxBounds.Doubles::new
|
|
);
|
|
|
|
private Doubles(Optional<Double> min, Optional<Double> max) {
|
|
this(min, max, squareOpt(min), squareOpt(max));
|
|
}
|
|
|
|
private static MinMaxBounds.Doubles create(StringReader reader, Optional<Double> min, Optional<Double> max) throws CommandSyntaxException {
|
|
if (min.isPresent() && max.isPresent() && (Double)min.get() > (Double)max.get()) {
|
|
throw ERROR_SWAPPED.createWithContext(reader);
|
|
} else {
|
|
return new MinMaxBounds.Doubles(min, max);
|
|
}
|
|
}
|
|
|
|
private static Optional<Double> squareOpt(Optional<Double> value) {
|
|
return value.map(double_ -> double_ * double_);
|
|
}
|
|
|
|
public static MinMaxBounds.Doubles exactly(double value) {
|
|
return new MinMaxBounds.Doubles(Optional.of(value), Optional.of(value));
|
|
}
|
|
|
|
public static MinMaxBounds.Doubles between(double min, double max) {
|
|
return new MinMaxBounds.Doubles(Optional.of(min), Optional.of(max));
|
|
}
|
|
|
|
public static MinMaxBounds.Doubles atLeast(double min) {
|
|
return new MinMaxBounds.Doubles(Optional.of(min), Optional.empty());
|
|
}
|
|
|
|
public static MinMaxBounds.Doubles atMost(double max) {
|
|
return new MinMaxBounds.Doubles(Optional.empty(), Optional.of(max));
|
|
}
|
|
|
|
public boolean matches(double value) {
|
|
return this.min.isPresent() && this.min.get() > value ? false : this.max.isEmpty() || !((Double)this.max.get() < value);
|
|
}
|
|
|
|
public boolean matchesSqr(double value) {
|
|
return this.minSq.isPresent() && this.minSq.get() > value ? false : this.maxSq.isEmpty() || !((Double)this.maxSq.get() < value);
|
|
}
|
|
|
|
public static MinMaxBounds.Doubles fromReader(StringReader reader) throws CommandSyntaxException {
|
|
return fromReader(reader, double_ -> double_);
|
|
}
|
|
|
|
public static MinMaxBounds.Doubles fromReader(StringReader reader, Function<Double, Double> formatter) throws CommandSyntaxException {
|
|
return MinMaxBounds.fromReader(
|
|
reader, MinMaxBounds.Doubles::create, Double::parseDouble, CommandSyntaxException.BUILT_IN_EXCEPTIONS::readerInvalidDouble, (Function<T, T>)formatter
|
|
);
|
|
}
|
|
}
|
|
|
|
public record Ints(Optional<Integer> min, Optional<Integer> max, Optional<Long> minSq, Optional<Long> maxSq) implements MinMaxBounds<Integer> {
|
|
public static final MinMaxBounds.Ints ANY = new MinMaxBounds.Ints(Optional.empty(), Optional.empty());
|
|
public static final Codec<MinMaxBounds.Ints> CODEC = MinMaxBounds.createCodec((Codec<T>)Codec.INT, MinMaxBounds.Ints::new);
|
|
public static final StreamCodec<ByteBuf, MinMaxBounds.Ints> STREAM_CODEC = MinMaxBounds.createStreamCodec(
|
|
(StreamCodec<ByteBuf, T>)ByteBufCodecs.INT, MinMaxBounds.Ints::new
|
|
);
|
|
|
|
private Ints(Optional<Integer> min, Optional<Integer> max) {
|
|
this(min, max, min.map(integer -> integer.longValue() * integer.longValue()), squareOpt(max));
|
|
}
|
|
|
|
private static MinMaxBounds.Ints create(StringReader reader, Optional<Integer> min, Optional<Integer> max) throws CommandSyntaxException {
|
|
if (min.isPresent() && max.isPresent() && (Integer)min.get() > (Integer)max.get()) {
|
|
throw ERROR_SWAPPED.createWithContext(reader);
|
|
} else {
|
|
return new MinMaxBounds.Ints(min, max);
|
|
}
|
|
}
|
|
|
|
private static Optional<Long> squareOpt(Optional<Integer> value) {
|
|
return value.map(integer -> integer.longValue() * integer.longValue());
|
|
}
|
|
|
|
public static MinMaxBounds.Ints exactly(int value) {
|
|
return new MinMaxBounds.Ints(Optional.of(value), Optional.of(value));
|
|
}
|
|
|
|
public static MinMaxBounds.Ints between(int min, int max) {
|
|
return new MinMaxBounds.Ints(Optional.of(min), Optional.of(max));
|
|
}
|
|
|
|
public static MinMaxBounds.Ints atLeast(int min) {
|
|
return new MinMaxBounds.Ints(Optional.of(min), Optional.empty());
|
|
}
|
|
|
|
public static MinMaxBounds.Ints atMost(int max) {
|
|
return new MinMaxBounds.Ints(Optional.empty(), Optional.of(max));
|
|
}
|
|
|
|
public boolean matches(int value) {
|
|
return this.min.isPresent() && this.min.get() > value ? false : this.max.isEmpty() || (Integer)this.max.get() >= value;
|
|
}
|
|
|
|
public boolean matchesSqr(long value) {
|
|
return this.minSq.isPresent() && this.minSq.get() > value ? false : this.maxSq.isEmpty() || (Long)this.maxSq.get() >= value;
|
|
}
|
|
|
|
public static MinMaxBounds.Ints fromReader(StringReader reader) throws CommandSyntaxException {
|
|
return fromReader(reader, integer -> integer);
|
|
}
|
|
|
|
public static MinMaxBounds.Ints fromReader(StringReader reader, Function<Integer, Integer> valueFunction) throws CommandSyntaxException {
|
|
return MinMaxBounds.fromReader(
|
|
reader, MinMaxBounds.Ints::create, Integer::parseInt, CommandSyntaxException.BUILT_IN_EXCEPTIONS::readerInvalidInt, (Function<T, T>)valueFunction
|
|
);
|
|
}
|
|
}
|
|
}
|