package net.minecraft.client; import com.google.common.collect.ImmutableList; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.DoubleFunction; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.IntSupplier; import java.util.function.Supplier; import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; import java.util.stream.IntStream; import javax.annotation.Nullable; import net.minecraft.Util; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractOptionSliderButton; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.CycleButton; import net.minecraft.client.gui.components.Tooltip; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; import net.minecraft.util.OptionEnum; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.slf4j.Logger; @OnlyIn(Dist.CLIENT) public final class OptionInstance { private static final Logger LOGGER = LogUtils.getLogger(); public static final OptionInstance.Enum BOOLEAN_VALUES = new OptionInstance.Enum<>(ImmutableList.of(Boolean.TRUE, Boolean.FALSE), Codec.BOOL); public static final OptionInstance.CaptionBasedToString BOOLEAN_TO_STRING = (p_231544_, p_231545_) -> p_231545_ ? CommonComponents.OPTION_ON : CommonComponents.OPTION_OFF; private final OptionInstance.TooltipSupplier tooltip; final Function toString; private final OptionInstance.ValueSet values; private final Codec codec; private final T initialValue; private final Consumer onValueUpdate; final Component caption; T value; public static OptionInstance createBoolean(String p_231529_, boolean p_231530_, Consumer p_231531_) { return createBoolean(p_231529_, noTooltip(), p_231530_, p_231531_); } public static OptionInstance createBoolean(String p_231526_, boolean p_231527_) { return createBoolean(p_231526_, noTooltip(), p_231527_, p_231548_ -> { }); } public static OptionInstance createBoolean(String p_259291_, OptionInstance.TooltipSupplier p_260306_, boolean p_259985_) { return createBoolean(p_259291_, p_260306_, p_259985_, p_231513_ -> { }); } public static OptionInstance createBoolean( String p_259289_, OptionInstance.TooltipSupplier p_260210_, boolean p_259359_, Consumer p_259975_ ) { return createBoolean(p_259289_, p_260210_, BOOLEAN_TO_STRING, p_259359_, p_259975_); } public static OptionInstance createBoolean( String p_262002_, OptionInstance.TooltipSupplier p_261507_, OptionInstance.CaptionBasedToString p_262099_, boolean p_262136_, Consumer p_261984_ ) { return new OptionInstance<>(p_262002_, p_261507_, p_262099_, BOOLEAN_VALUES, p_262136_, p_261984_); } public OptionInstance( String p_260248_, OptionInstance.TooltipSupplier p_259437_, OptionInstance.CaptionBasedToString p_259148_, OptionInstance.ValueSet p_259590_, T p_260067_, Consumer p_259392_ ) { this(p_260248_, p_259437_, p_259148_, p_259590_, p_259590_.codec(), p_260067_, p_259392_); } public OptionInstance( String p_259964_, OptionInstance.TooltipSupplier p_260354_, OptionInstance.CaptionBasedToString p_259496_, OptionInstance.ValueSet p_259090_, Codec p_259043_, T p_259396_, Consumer p_260147_ ) { this.caption = Component.translatable(p_259964_); this.tooltip = p_260354_; this.toString = p_231506_ -> p_259496_.toString(this.caption, p_231506_); this.values = p_259090_; this.codec = p_259043_; this.initialValue = p_259396_; this.onValueUpdate = p_260147_; this.value = this.initialValue; } public static OptionInstance.TooltipSupplier noTooltip() { return p_258114_ -> null; } public static OptionInstance.TooltipSupplier cachedConstantTooltip(Component p_231536_) { return p_258116_ -> Tooltip.create(p_231536_); } public static OptionInstance.CaptionBasedToString forOptionEnum() { return (p_231538_, p_231539_) -> p_231539_.getCaption(); } public AbstractWidget createButton(Options p_332722_) { return this.createButton(p_332722_, 0, 0, 150); } public AbstractWidget createButton(Options p_231508_, int p_231509_, int p_231510_, int p_231511_) { return this.createButton(p_231508_, p_231509_, p_231510_, p_231511_, p_261336_ -> { }); } public AbstractWidget createButton(Options p_261971_, int p_261486_, int p_261569_, int p_261677_, Consumer p_261912_) { return this.values.createButton(this.tooltip, p_261971_, p_261486_, p_261569_, p_261677_, p_261912_).apply(this); } public T get() { return this.value; } public Codec codec() { return this.codec; } @Override public String toString() { return this.caption.getString(); } public void set(T p_231515_) { T t = this.values.validateValue(p_231515_).orElseGet(() -> { LOGGER.error("Illegal option value " + p_231515_ + " for " + this.caption); return this.initialValue; }); if (!Minecraft.getInstance().isRunning()) { this.value = t; } else { if (!Objects.equals(this.value, t)) { this.value = t; this.onValueUpdate.accept(this.value); } } } public OptionInstance.ValueSet values() { return this.values; } @OnlyIn(Dist.CLIENT) public static record AltEnum( List values, List altValues, BooleanSupplier altCondition, OptionInstance.CycleableValueSet.ValueSetter valueSetter, Codec codec ) implements OptionInstance.CycleableValueSet { @Override public CycleButton.ValueListSupplier valueListSupplier() { return CycleButton.ValueListSupplier.create(this.altCondition, this.values, this.altValues); } @Override public Optional validateValue(T p_231570_) { return (this.altCondition.getAsBoolean() ? this.altValues : this.values).contains(p_231570_) ? Optional.of(p_231570_) : Optional.empty(); } @Override public OptionInstance.CycleableValueSet.ValueSetter valueSetter() { return this.valueSetter; } @Override public Codec codec() { return this.codec; } } @OnlyIn(Dist.CLIENT) public interface CaptionBasedToString { Component toString(Component p_231581_, T p_231582_); } @OnlyIn(Dist.CLIENT) public static record ClampingLazyMaxIntRange(int minInclusive, IntSupplier maxSupplier, int encodableMaxInclusive) implements OptionInstance.IntRangeBase, OptionInstance.SliderableOrCyclableValueSet { public Optional validateValue(Integer p_231590_) { return Optional.of(Mth.clamp(p_231590_, this.minInclusive(), this.maxInclusive())); } @Override public int maxInclusive() { return this.maxSupplier.getAsInt(); } @Override public Codec codec() { return Codec.INT .validate( p_276098_ -> { int i = this.encodableMaxInclusive + 1; return p_276098_.compareTo(this.minInclusive) >= 0 && p_276098_.compareTo(i) <= 0 ? DataResult.success(p_276098_) : DataResult.error(() -> "Value " + p_276098_ + " outside of range [" + this.minInclusive + ":" + i + "]", p_276098_); } ); } @Override public boolean createCycleButton() { return true; } @Override public CycleButton.ValueListSupplier valueListSupplier() { return CycleButton.ValueListSupplier.create(IntStream.range(this.minInclusive, this.maxInclusive() + 1).boxed().toList()); } @Override public int minInclusive() { return this.minInclusive; } } @OnlyIn(Dist.CLIENT) interface CycleableValueSet extends OptionInstance.ValueSet { CycleButton.ValueListSupplier valueListSupplier(); default OptionInstance.CycleableValueSet.ValueSetter valueSetter() { return OptionInstance::set; } @Override default Function, AbstractWidget> createButton( OptionInstance.TooltipSupplier p_261801_, Options p_261824_, int p_261649_, int p_262114_, int p_261536_, Consumer p_261642_ ) { return p_261343_ -> CycleButton.builder(p_261343_.toString) .withValues(this.valueListSupplier()) .withTooltip(p_261801_) .withInitialValue(p_261343_.value) .create(p_261649_, p_262114_, p_261536_, 20, p_261343_.caption, (p_261347_, p_261348_) -> { this.valueSetter().set(p_261343_, p_261348_); p_261824_.save(); p_261642_.accept(p_261348_); }); } @OnlyIn(Dist.CLIENT) public interface ValueSetter { void set(OptionInstance p_231623_, T p_231624_); } } @OnlyIn(Dist.CLIENT) public static record Enum(List values, Codec codec) implements OptionInstance.CycleableValueSet { @Override public Optional validateValue(T p_231632_) { return this.values.contains(p_231632_) ? Optional.of(p_231632_) : Optional.empty(); } @Override public CycleButton.ValueListSupplier valueListSupplier() { return CycleButton.ValueListSupplier.create(this.values); } @Override public Codec codec() { return this.codec; } } @OnlyIn(Dist.CLIENT) public static record IntRange(int minInclusive, int maxInclusive, boolean applyValueImmediately) implements OptionInstance.IntRangeBase { public IntRange(int p_231642_, int p_231643_) { this(p_231642_, p_231643_, true); } public Optional validateValue(Integer p_231645_) { return p_231645_.compareTo(this.minInclusive()) >= 0 && p_231645_.compareTo(this.maxInclusive()) <= 0 ? Optional.of(p_231645_) : Optional.empty(); } @Override public Codec codec() { return Codec.intRange(this.minInclusive, this.maxInclusive + 1); } @Override public int minInclusive() { return this.minInclusive; } @Override public int maxInclusive() { return this.maxInclusive; } @Override public boolean applyValueImmediately() { return this.applyValueImmediately; } } @OnlyIn(Dist.CLIENT) interface IntRangeBase extends OptionInstance.SliderableValueSet { int minInclusive(); int maxInclusive(); default double toSliderValue(Integer p_231663_) { if (p_231663_ == this.minInclusive()) { return 0.0; } else { return p_231663_ == this.maxInclusive() ? 1.0 : Mth.map((double)p_231663_.intValue() + 0.5, (double)this.minInclusive(), (double)this.maxInclusive() + 1.0, 0.0, 1.0); } } default Integer fromSliderValue(double p_231656_) { if (p_231656_ >= 1.0) { p_231656_ = 0.99999F; } return Mth.floor(Mth.map(p_231656_, 0.0, 1.0, (double)this.minInclusive(), (double)this.maxInclusive() + 1.0)); } default OptionInstance.SliderableValueSet xmap(final IntFunction p_231658_, final ToIntFunction p_231659_) { return new OptionInstance.SliderableValueSet() { @Override public Optional validateValue(R p_231674_) { return IntRangeBase.this.validateValue(Integer.valueOf(p_231659_.applyAsInt(p_231674_))).map(p_231658_::apply); } @Override public double toSliderValue(R p_231678_) { return IntRangeBase.this.toSliderValue(p_231659_.applyAsInt(p_231678_)); } @Override public R fromSliderValue(double p_231676_) { return (R)p_231658_.apply(IntRangeBase.this.fromSliderValue(p_231676_)); } @Override public Codec codec() { return IntRangeBase.this.codec().xmap(p_231658_::apply, p_231659_::applyAsInt); } }; } } @OnlyIn(Dist.CLIENT) public static record LazyEnum(Supplier> values, Function> validateValue, Codec codec) implements OptionInstance.CycleableValueSet { @Override public Optional validateValue(T p_231689_) { return this.validateValue.apply(p_231689_); } @Override public CycleButton.ValueListSupplier valueListSupplier() { return CycleButton.ValueListSupplier.create(this.values.get()); } @Override public Codec codec() { return this.codec; } } @OnlyIn(Dist.CLIENT) public static final class OptionInstanceSliderButton extends AbstractOptionSliderButton { private final OptionInstance instance; private final OptionInstance.SliderableValueSet values; private final OptionInstance.TooltipSupplier tooltipSupplier; private final Consumer onValueChanged; @Nullable private Long delayedApplyAt; private final boolean applyValueImmediately; OptionInstanceSliderButton( Options p_261713_, int p_261873_, int p_261656_, int p_261799_, int p_261893_, OptionInstance p_262129_, OptionInstance.SliderableValueSet p_261995_, OptionInstance.TooltipSupplier p_261963_, Consumer p_261829_, boolean p_332382_ ) { super(p_261713_, p_261873_, p_261656_, p_261799_, p_261893_, p_261995_.toSliderValue(p_262129_.get())); this.instance = p_262129_; this.values = p_261995_; this.tooltipSupplier = p_261963_; this.onValueChanged = p_261829_; this.applyValueImmediately = p_332382_; this.updateMessage(); } @Override protected void updateMessage() { this.setMessage(this.instance.toString.apply(this.values.fromSliderValue(this.value))); this.setTooltip(this.tooltipSupplier.apply(this.values.fromSliderValue(this.value))); } @Override protected void applyValue() { if (this.applyValueImmediately) { this.applyUnsavedValue(); } else { this.delayedApplyAt = Util.getMillis() + 600L; } } public void applyUnsavedValue() { N n = this.values.fromSliderValue(this.value); if (!Objects.equals(n, this.instance.get())) { this.instance.set(n); this.onValueChanged.accept(this.instance.get()); } } @Override public void renderWidget(GuiGraphics p_332467_, int p_329907_, int p_334179_, float p_329288_) { super.renderWidget(p_332467_, p_329907_, p_334179_, p_329288_); if (this.delayedApplyAt != null && Util.getMillis() >= this.delayedApplyAt) { this.delayedApplyAt = null; this.applyUnsavedValue(); } } } @OnlyIn(Dist.CLIENT) interface SliderableOrCyclableValueSet extends OptionInstance.CycleableValueSet, OptionInstance.SliderableValueSet { boolean createCycleButton(); @Override default Function, AbstractWidget> createButton( OptionInstance.TooltipSupplier p_261786_, Options p_262030_, int p_261940_, int p_262149_, int p_261495_, Consumer p_261881_ ) { return this.createCycleButton() ? OptionInstance.CycleableValueSet.super.createButton(p_261786_, p_262030_, p_261940_, p_262149_, p_261495_, p_261881_) : OptionInstance.SliderableValueSet.super.createButton(p_261786_, p_262030_, p_261940_, p_262149_, p_261495_, p_261881_); } } @OnlyIn(Dist.CLIENT) interface SliderableValueSet extends OptionInstance.ValueSet { double toSliderValue(T p_231732_); T fromSliderValue(double p_231731_); default boolean applyValueImmediately() { return true; } @Override default Function, AbstractWidget> createButton( OptionInstance.TooltipSupplier p_261993_, Options p_262177_, int p_261706_, int p_261683_, int p_261573_, Consumer p_261969_ ) { return p_325282_ -> new OptionInstance.OptionInstanceSliderButton<>( p_262177_, p_261706_, p_261683_, p_261573_, 20, p_325282_, this, p_261993_, p_261969_, this.applyValueImmediately() ); } } @FunctionalInterface @OnlyIn(Dist.CLIENT) public interface TooltipSupplier { @Nullable Tooltip apply(T p_259319_); } @OnlyIn(Dist.CLIENT) public static enum UnitDouble implements OptionInstance.SliderableValueSet { INSTANCE; public Optional validateValue(Double p_231747_) { return p_231747_ >= 0.0 && p_231747_ <= 1.0 ? Optional.of(p_231747_) : Optional.empty(); } public double toSliderValue(Double p_231756_) { return p_231756_; } public Double fromSliderValue(double p_231741_) { return p_231741_; } public OptionInstance.SliderableValueSet xmap(final DoubleFunction p_231751_, final ToDoubleFunction p_231752_) { return new OptionInstance.SliderableValueSet() { @Override public Optional validateValue(R p_231773_) { return UnitDouble.this.validateValue(p_231752_.applyAsDouble(p_231773_)).map(p_231751_::apply); } @Override public double toSliderValue(R p_231777_) { return UnitDouble.this.toSliderValue(p_231752_.applyAsDouble(p_231777_)); } @Override public R fromSliderValue(double p_231775_) { return (R)p_231751_.apply(UnitDouble.this.fromSliderValue(p_231775_)); } @Override public Codec codec() { return UnitDouble.this.codec().xmap(p_231751_::apply, p_231752_::applyAsDouble); } }; } @Override public Codec codec() { return Codec.withAlternative(Codec.doubleRange(0.0, 1.0), Codec.BOOL, p_231745_ -> p_231745_ ? 1.0 : 0.0); } } @OnlyIn(Dist.CLIENT) interface ValueSet { Function, AbstractWidget> createButton( OptionInstance.TooltipSupplier p_231779_, Options p_231780_, int p_231781_, int p_231782_, int p_231783_, Consumer p_261976_ ); Optional validateValue(T p_231784_); Codec codec(); } }