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 { SimpleCommandExceptionType ERROR_EMPTY = new SimpleCommandExceptionType(Component.translatable("argument.range.empty")); SimpleCommandExceptionType ERROR_SWAPPED = new SimpleCommandExceptionType(Component.translatable("argument.range.swapped")); Optional min(); Optional max(); default boolean isAny() { return this.min().isEmpty() && this.max().isEmpty(); } default Optional unwrapPoint() { Optional optional = this.min(); Optional optional2 = this.max(); return optional.equals(optional2) ? optional : Optional.empty(); } static > Codec createCodec(Codec codec, MinMaxBounds.BoundsFactory boundsFactory) { Codec 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 optional = minMaxBounds.unwrapPoint(); return optional.isPresent() ? Either.right((Number)optional.get()) : Either.left(minMaxBounds); }); } static > StreamCodec createStreamCodec( StreamCodec streamCodec, MinMaxBounds.BoundsFactory boundsFactory ) { return new StreamCodec() { private static final int MIN_FLAG = 1; public static final int MAX_FLAG = 2; public R decode(B byteBuf) { byte b = byteBuf.readByte(); Optional optional = (b & 1) != 0 ? Optional.of(streamCodec.decode(byteBuf)) : Optional.empty(); Optional optional2 = (b & 2) != 0 ? Optional.of(streamCodec.decode(byteBuf)) : Optional.empty(); return boundsFactory.create(optional, optional2); } public void encode(B byteBuf, R minMaxBounds) { Optional optional = minMaxBounds.min(); Optional 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 > R fromReader( StringReader reader, MinMaxBounds.BoundsFromReaderFactory boundedFactory, Function valueFactory, Supplier commandExceptionSupplier, Function formatter ) throws CommandSyntaxException { if (!reader.canRead()) { throw ERROR_EMPTY.createWithContext(reader); } else { int i = reader.getCursor(); try { Optional optional = readNumber(reader, valueFactory, commandExceptionSupplier).map(formatter); Optional 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 Optional readNumber( StringReader reader, Function stringToValueFunction, Supplier 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> { R create(Optional optional, Optional optional2); } @FunctionalInterface public interface BoundsFromReaderFactory> { R create(StringReader stringReader, Optional optional, Optional optional2) throws CommandSyntaxException; } public record Doubles(Optional min, Optional max, Optional minSq, Optional maxSq) implements MinMaxBounds { public static final MinMaxBounds.Doubles ANY = new MinMaxBounds.Doubles(Optional.empty(), Optional.empty()); public static final Codec CODEC = MinMaxBounds.createCodec((Codec)Codec.DOUBLE, MinMaxBounds.Doubles::new); public static final StreamCodec STREAM_CODEC = MinMaxBounds.createStreamCodec( (StreamCodec)ByteBufCodecs.DOUBLE, MinMaxBounds.Doubles::new ); private Doubles(Optional min, Optional max) { this(min, max, squareOpt(min), squareOpt(max)); } private static MinMaxBounds.Doubles create(StringReader reader, Optional min, Optional 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 squareOpt(Optional 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 formatter) throws CommandSyntaxException { return MinMaxBounds.fromReader( reader, MinMaxBounds.Doubles::create, Double::parseDouble, CommandSyntaxException.BUILT_IN_EXCEPTIONS::readerInvalidDouble, (Function)formatter ); } } public record Ints(Optional min, Optional max, Optional minSq, Optional maxSq) implements MinMaxBounds { public static final MinMaxBounds.Ints ANY = new MinMaxBounds.Ints(Optional.empty(), Optional.empty()); public static final Codec CODEC = MinMaxBounds.createCodec((Codec)Codec.INT, MinMaxBounds.Ints::new); public static final StreamCodec STREAM_CODEC = MinMaxBounds.createStreamCodec( (StreamCodec)ByteBufCodecs.INT, MinMaxBounds.Ints::new ); private Ints(Optional min, Optional max) { this(min, max, min.map(integer -> integer.longValue() * integer.longValue()), squareOpt(max)); } private static MinMaxBounds.Ints create(StringReader reader, Optional min, Optional 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 squareOpt(Optional 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 valueFunction) throws CommandSyntaxException { return MinMaxBounds.fromReader( reader, MinMaxBounds.Ints::create, Integer::parseInt, CommandSyntaxException.BUILT_IN_EXCEPTIONS::readerInvalidInt, (Function)valueFunction ); } } }