diff options
Diffstat (limited to 'common')
3 files changed, 67 insertions, 8 deletions
diff --git a/common/src/main/java/dev/isxander/yacl3/impl/ListOptionEntryImpl.java b/common/src/main/java/dev/isxander/yacl3/impl/ListOptionEntryImpl.java index bdb8de8..72254cf 100644 --- a/common/src/main/java/dev/isxander/yacl3/impl/ListOptionEntryImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/impl/ListOptionEntryImpl.java @@ -138,7 +138,7 @@ public final class ListOptionEntryImpl<T> implements ListOptionEntry<T> { @Override public void setValue(T newValue) { value = newValue; - group.callListeners(); + group.callListeners(true); } @Override diff --git a/common/src/main/java/dev/isxander/yacl3/impl/ListOptionImpl.java b/common/src/main/java/dev/isxander/yacl3/impl/ListOptionImpl.java index 12842c1..f45e368 100644 --- a/common/src/main/java/dev/isxander/yacl3/impl/ListOptionImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/impl/ListOptionImpl.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.impl.utils.YACLConstants; import net.minecraft.network.chat.Component; import org.apache.commons.lang3.Validate; import org.jetbrains.annotations.ApiStatus; @@ -30,8 +31,10 @@ public final class ListOptionImpl<T> implements ListOption<T> { private final boolean insertEntriesAtEnd; private final ImmutableSet<OptionFlag> flags; private final EntryFactory entryFactory; + private final List<BiConsumer<Option<List<T>>, List<T>>> listeners; private final List<Runnable> refreshListeners; + private int listenerTriggerDepth = 0; public ListOptionImpl(@NotNull Component name, @NotNull OptionDescription description, @NotNull Binding<List<T>> binding, @NotNull Supplier<T> initialValue, @NotNull Function<ListOptionEntry<T>, Controller<T>> controllerFunction, ImmutableSet<OptionFlag> flags, boolean collapsed, boolean available, int minimumNumberOfEntries, int maximumNumberOfEntries, boolean insertEntriesAtEnd, Collection<BiConsumer<Option<List<T>>, List<T>>> listeners) { this.name = name; @@ -49,7 +52,7 @@ public final class ListOptionImpl<T> implements ListOption<T> { this.listeners = new ArrayList<>(); this.listeners.addAll(listeners); this.refreshListeners = new ArrayList<>(); - callListeners(); + callListeners(true); } @Override @@ -170,7 +173,12 @@ public final class ListOptionImpl<T> implements ListOption<T> { @Override public void setAvailable(boolean available) { + boolean changed = this.available != available; + this.available = available; + + if (changed) + callListeners(false); } @Override @@ -205,14 +213,30 @@ public final class ListOptionImpl<T> implements ListOption<T> { return values.stream().map(entryFactory::create).collect(Collectors.toList()); } - void callListeners() { + void callListeners(boolean bypass) { List<T> pendingValue = pendingValue(); - this.listeners.forEach(listener -> listener.accept(this, pendingValue)); + if (bypass || listenerTriggerDepth == 0) { + if (listenerTriggerDepth > 10) { + throw new IllegalStateException("Listener trigger depth exceeded 10! This means a listener triggered a listener etc etc 10 times deep. This is likely a bug in the mod using YACL!"); + } + + this.listenerTriggerDepth++; + + for (BiConsumer<Option<List<T>>, List<T>> listener : listeners) { + try { + listener.accept(this, pendingValue); + } catch (Exception e) { + YACLConstants.LOGGER.error("Exception whilst triggering listener for option '%s'".formatted(name.getString()), e); + } + } + + this.listenerTriggerDepth--; + } } private void onRefresh() { refreshListeners.forEach(Runnable::run); - callListeners(); + callListeners(true); } private class EntryFactory { @@ -234,7 +258,7 @@ public final class ListOptionImpl<T> implements ListOption<T> { private Function<ListOptionEntry<T>, Controller<T>> controllerFunction; private Binding<List<T>> binding = null; private final Set<OptionFlag> flags = new HashSet<>(); - private T initialValue; + private Supplier<T> initialValue; private boolean collapsed = false; private boolean available = true; private int minimumNumberOfEntries = 0; @@ -259,7 +283,7 @@ public final class ListOptionImpl<T> implements ListOption<T> { } @Override - public Builder<T> initial(@NotNull T initialValue) { + public Builder<T> initial(@NotNull Supplier<T> initialValue) { Validate.notNull(initialValue, "`initialValue` cannot be empty"); this.initialValue = initialValue; @@ -267,6 +291,14 @@ public final class ListOptionImpl<T> implements ListOption<T> { } @Override + public Builder<T> initial(@NotNull T initialValue) { + Validate.notNull(initialValue, "`initialValue` cannot be empty"); + + this.initialValue = () -> initialValue; + return this; + } + + @Override public Builder<T> controller(@NotNull Function<Option<T>, ControllerBuilder<T>> controller) { Validate.notNull(controller, "`controller` cannot be null"); diff --git a/common/src/main/java/dev/isxander/yacl3/impl/OptionImpl.java b/common/src/main/java/dev/isxander/yacl3/impl/OptionImpl.java index 01a6287..165f38d 100644 --- a/common/src/main/java/dev/isxander/yacl3/impl/OptionImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/impl/OptionImpl.java @@ -3,6 +3,7 @@ package dev.isxander.yacl3.impl; import com.google.common.collect.ImmutableSet; import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.impl.utils.YACLConstants; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import org.apache.commons.lang3.Validate; @@ -28,6 +29,7 @@ public final class OptionImpl<T> implements Option<T> { private T pendingValue; private final List<BiConsumer<Option<T>, T>> listeners; + private int listenerTriggerDepth = 0; public OptionImpl( @NotNull Component name, @@ -82,7 +84,12 @@ public final class OptionImpl<T> implements Option<T> { @Override public void setAvailable(boolean available) { + boolean changed = this.available != available; + this.available = available; + + if (changed) + this.triggerListeners(false); } @Override @@ -103,7 +110,7 @@ public final class OptionImpl<T> implements Option<T> { @Override public void requestSet(T value) { pendingValue = value; - listeners.forEach(listener -> listener.accept(this, pendingValue)); + this.triggerListeners(true); } @Override @@ -135,6 +142,26 @@ public final class OptionImpl<T> implements Option<T> { this.listeners.add(changedListener); } + private void triggerListeners(boolean bypass) { + if (bypass || listenerTriggerDepth == 0) { + if (listenerTriggerDepth > 10) { + throw new IllegalStateException("Listener trigger depth exceeded 10! This means a listener triggered a listener etc etc 10 times deep. This is likely a bug in the mod using YACL!"); + } + + this.listenerTriggerDepth++; + + for (BiConsumer<Option<T>, T> listener : listeners) { + try { + listener.accept(this, pendingValue); + } catch (Exception e) { + YACLConstants.LOGGER.error("Exception whilst triggering listener for option '%s'".formatted(name.getString()), e); + } + } + + this.listenerTriggerDepth--; + } + } + @ApiStatus.Internal public static class BuilderImpl<T> implements Builder<T> { private Component name = Component.literal("Name not specified!").withStyle(ChatFormatting.RED); |