diff --git a/README.MD b/README.MD index 09dcf7d606..1fea3b6667 100644 --- a/README.MD +++ b/README.MD @@ -73,7 +73,7 @@ A Fabric client-side QoL mod for the Hypixel SkyBlock Bazaar. Other - **Modify Indicator**: Marks anything Bazaar Utils adds or changes on an item with a small ₿, so it's always clear what's the mod and what's Hypixel. Configurable display type. -- **Auto Update**: Automatically updates the mod when a new version is found (toggleable in Advanced Settings). +- **Auto Update**: Automatically updates the mod when a new version is found (toggleable in the main settings). - **Bazaar Flipper Setting**: Set your Bazaar Flipper account upgrade tier in the config so tax calculations and order limits are accurate. - **Mod Menu Integration**: Config accessible through Mod Menu, with mod-provided config widgets. - **Developer Mode**: Optional developer tools, debug messages, and error-notification toggles. diff --git a/UPDATES.MD b/UPDATES.MD index 0cdcd4f4b6..2caded63bf 100644 --- a/UPDATES.MD +++ b/UPDATES.MD @@ -3,6 +3,10 @@ The first full release of Bazaar Utils. Almost every part of the mod has been re settings screen, the button system, order tracking, and how the mod reads item data. There are many new features, and just as many old features that were improved. +Advanced configuration mode now defaults to off for new configs. Enable it to customize button +sizes, spacing, icons and slots, choose individual Bazaar messages to hide, reset tracked Bazaar +limits, or access developer settings. Hiding these controls preserves their configured behavior. + --- ## ⚠️ Please read before updating **Your settings will not carry over.** v1.0.0 uses a completely new config system and saves to a new @@ -52,7 +56,7 @@ what's the mod and what's Hypixel. Show it as a name prefix, a name suffix, the only on modified items, or turn it off entirely. ### Auto Update The mod can now update itself when a new version is released, matching the release channel you're -already on (alpha/beta/stable). On by default; toggle it in **Advanced Settings**. +already on (alpha/beta/stable). On by default; toggle it in the main settings. ### Translations The mod is now fully translated into **German, Polish, French, Simplified Chinese and Russian**, and follows your Minecraft language setting. @@ -107,4 +111,4 @@ daily limits are not touched. ## 🙏 Thanks Huge thanks to **@0xar-ds** for the architectural work behind this release, and to the many others who helped write code, find bugs, and suggest features. -Questions or problems? Join the [Discord](https://discord.gg/xDKjvm5hQd). \ No newline at end of file +Questions or problems? Join the [Discord](https://discord.gg/xDKjvm5hQd). diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/BUConfig.java b/src/main/java/com/github/mkram17/bazaarutils/config/BUConfig.java index afba5e6ad2..679ec64b7a 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/BUConfig.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/BUConfig.java @@ -1,6 +1,5 @@ package com.github.mkram17.bazaarutils.config; -import com.github.mkram17.bazaarutils.config.features.AdvancedConfig; import com.github.mkram17.bazaarutils.config.hidden.MetadataConfig; import com.github.mkram17.bazaarutils.config.features.DeveloperConfig; import com.github.mkram17.bazaarutils.config.features.chat.ChatConfig; @@ -12,6 +11,7 @@ import com.github.mkram17.bazaarutils.utils.bazaar.PlayerAccountUpgrades; import com.github.mkram17.bazaarutils.utils.minecraft.item.modifier.ModifyIndicator; import com.teamresourceful.resourcefulconfig.api.annotations.*; +import com.teamresourceful.resourcefulconfig.api.types.entries.Observable; import static com.github.mkram17.bazaarutils.BazaarUtils.MOD_ID; @@ -25,7 +25,6 @@ InventoryConfig.class, OverlaysConfig.class, NotificationsConfig.class, - AdvancedConfig.class, DeveloperConfig.class }, version = ConfigUtil.VERSION @@ -57,6 +56,26 @@ public static BUConfig get(){ ) public static boolean INTRODUCTORY_INFORMATION_SEPARATOR = true; + @ConfigEntry( + id = "advanced_configuration_mode", + translation = "bazaarutils.config.advanced_configuration_mode.label" + ) + @Comment( + value = "Certain feature customization is hidden away from a normal configuration of the mod as they're options generally of no interest. Here you can toggle to see and configure them as well.", + translation = "bazaarutils.config.advanced_configuration_mode.hint" + ) + public static Observable ADVANCED_CONFIGURATION_TOGGLE = Observable.of(false); + + @ConfigEntry( + id = "automatic_updates", + translation = "bazaarutils.config.automatic_updates.label" + ) + @Comment( + value = "Automatically update the mod when an update is found.", + translation = "bazaarutils.config.automatic_updates.hint" + ) + public static boolean AUTOMATIC_UPDATES_TOGGLE = true; + @ConfigEntry( id = "bazaar_flipper_account_upgrade", translation = "bazaarutils.config.bazaar_flipper_account_upgrade.label" @@ -86,4 +105,4 @@ public static BUConfig get(){ title = "bazaarutils.config.reset_config.label" ) public static final Runnable RESET_CONFIG_BUTTON = ConfigUtil::confirmResetToDefaults; -} \ No newline at end of file +} diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/features/AdvancedConfig.java b/src/main/java/com/github/mkram17/bazaarutils/config/features/AdvancedConfig.java deleted file mode 100644 index 98e160a624..0000000000 --- a/src/main/java/com/github/mkram17/bazaarutils/config/features/AdvancedConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.github.mkram17.bazaarutils.config.features; - -import com.teamresourceful.resourcefulconfig.api.annotations.Category; -import com.teamresourceful.resourcefulconfig.api.annotations.Comment; -import com.teamresourceful.resourcefulconfig.api.annotations.ConfigEntry; -import com.teamresourceful.resourcefulconfig.api.annotations.ConfigInfo; - -@Category(value = "advanced_config") -@ConfigInfo( - title = "Advanced Settings", - titleTranslation = "bazaarutils.config.advanced.category.label", - description = "Advanced configurations for routines & features of the mod", - descriptionTranslation = "bazaarutils.config.advanced.category.hint", - icon = "curly-braces" -) -public class AdvancedConfig { - @ConfigEntry( - id = "auto_update", - translation = "bazaarutils.config.advanced.auto_update.label" - ) - @Comment( - value = "Automatically update the mod when an update is found.", - translation = "bazaarutils.config.advanced.auto_update.hint" - ) - public static boolean AUTO_UPDATE_TOGGLE = true; -} diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/features/DeveloperConfig.java b/src/main/java/com/github/mkram17/bazaarutils/config/features/DeveloperConfig.java index f67e56b0e3..ef8b3051c8 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/features/DeveloperConfig.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/features/DeveloperConfig.java @@ -1,5 +1,7 @@ package com.github.mkram17.bazaarutils.config.features; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.misc.NotificationType; import com.teamresourceful.resourcefulconfig.api.annotations.*; @@ -23,12 +25,14 @@ public final class DeveloperConfig { value = "Global toggle for all developer related functionalities/utilities.", translation = "bazaarutils.config.developer.enabled.hint" ) + @ShowIf(AdvancedConfigurationMode.class) public static boolean DEVELOPER_MODE_TOGGLE = false; @ConfigEntry( id = "disable_error_notifications", translation = "bazaarutils.config.developer.disable_error_notifications.label" ) + @ShowIf(AdvancedConfigurationMode.class) public static boolean DEVELOPER_MODE_DISABLE_ERROR_NOTIFICATIONS = false; @ConfigEntry( @@ -40,6 +44,7 @@ public final class DeveloperConfig { translation = "bazaarutils.config.developer.debug_messages.hint" ) @ConfigOption.Draggable + @ShowIf(AdvancedConfigurationMode.class) public static NotificationType[] DEVELOPER_MODE_DEBUG_MESSAGES = new NotificationType[]{}; public static boolean isDeveloperVariableEnabled(NotificationType type) { diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/features/chat/ChatConfig.java b/src/main/java/com/github/mkram17/bazaarutils/config/features/chat/ChatConfig.java index e16dbce3c5..fb76e557f7 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/features/chat/ChatConfig.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/features/chat/ChatConfig.java @@ -1,5 +1,7 @@ package com.github.mkram17.bazaarutils.config.features.chat; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.features.chat.StashMessagesRemover; import com.github.mkram17.bazaarutils.features.chat.UselessBazaarNotificationsRemover; import com.teamresourceful.resourcefulconfig.api.annotations.*; @@ -34,6 +36,7 @@ public final class ChatConfig { translation = "bazaarutils.config.chat.useless_bazaar_notifications_remover.excluded_notifications.hint" ) @ConfigOption.Draggable() + @ShowIf(AdvancedConfigurationMode.class) public static UselessBazaarNotificationsRemover.TransientBazaarMessages[] USELESS_BAZAAR_NOTIFICATIONS_REMOVER_EXCLUDED_NOTIFICATIONS = new UselessBazaarNotificationsRemover.TransientBazaarMessages[]{ UselessBazaarNotificationsRemover.TransientBazaarMessages.CANCELLING_ORDER, UselessBazaarNotificationsRemover.TransientBazaarMessages.PUTTING_GOODS_IN_ESCROW, diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/features/gui/ButtonsConfig.java b/src/main/java/com/github/mkram17/bazaarutils/config/features/gui/ButtonsConfig.java index eaf3a956ec..d509e950b2 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/features/gui/ButtonsConfig.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/features/gui/ButtonsConfig.java @@ -1,5 +1,7 @@ package com.github.mkram17.bazaarutils.config.features.gui; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.data.stored.BookmarksStorage; import com.github.mkram17.bazaarutils.features.gui.buttons.bookmarks.BookmarkUtil; import com.github.mkram17.bazaarutils.features.gui.buttons.inputhelper.amount.BuyOrderAmountHelper; @@ -195,12 +197,14 @@ public static final class WidgetButton { id = "size", translation = "bazaarutils.config.buttons.button.widget.size.label" ) + @ShowIf(AdvancedConfigurationMode.class) public int size = 18; @ConfigEntry( id = "spacing", translation = "bazaarutils.config.buttons.button.widget.spacing.label" ) + @ShowIf(AdvancedConfigurationMode.class) public int spacing = 4; public WidgetButton(boolean enabled) { @@ -231,6 +235,7 @@ public static final class SmallContainerButton { translation = "bazaarutils.config.buttons.button.container.item_id.hint" ) @ConfigOption.Renderer("bazaarutils:item") + @ShowIf(AdvancedConfigurationMode.class) public String itemId = "minecraft:green_stained_glass_pane"; @Getter @@ -244,6 +249,7 @@ public static final class SmallContainerButton { ) @ConfigOption.Range(min = 0, max = 35) @ConfigOption.Renderer("bazaarutils:slot") + @ShowIf(AdvancedConfigurationMode.class) public int slotIndex; public SmallContainerButton(boolean enabled, int slotIndex) { diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/features/gui/OverlaysConfig.java b/src/main/java/com/github/mkram17/bazaarutils/config/features/gui/OverlaysConfig.java index 87c2208aee..6ff2b4e540 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/features/gui/OverlaysConfig.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/features/gui/OverlaysConfig.java @@ -1,5 +1,7 @@ package com.github.mkram17.bazaarutils.config.features.gui; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.features.gui.overlays.BazaarLimitsVisualizer; import com.github.mkram17.bazaarutils.features.gui.overlays.PriceCharts; import com.github.mkram17.bazaarutils.features.gui.overlays.UserOrdersOverlay; @@ -55,6 +57,7 @@ public class OverlaysConfig { text = "bazaarutils.config.overlays.bazaar_limits_visualizer.reset_limits.runnable", title = "bazaarutils.config.overlays.bazaar_limits_visualizer.reset_limits.label" ) + @ShowIf(AdvancedConfigurationMode.class) public static final Runnable RESET_LIMITS_BUTTON = BazaarLimitsVisualizer::resetLimits; @ConfigEntry( diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/ConfigElementFieldStore.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/ConfigElementFieldStore.java new file mode 100644 index 0000000000..ffa18e5003 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/ConfigElementFieldStore.java @@ -0,0 +1,33 @@ +package com.github.mkram17.bazaarutils.config.util; + +import com.teamresourceful.resourcefulconfig.api.types.ResourcefulConfigElement; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Maps separators and buttons to the {@link Field} they were built from and the + * owner instance in scope at parse time, so their visibility conditions can be evaluated. + * + * @see com.github.mkram17.bazaarutils.mixin.JavaConfigParserMixin + */ +public final class ConfigElementFieldStore { + private ConfigElementFieldStore() {} + + public record Context(Field field, Optional owner) {} + + // Parsed separators and buttons are records; use identity so equal elements + // from different fields can still have different visibility conditions. + private static final Map REGISTRY = Collections.synchronizedMap(new IdentityHashMap<>()); + + public static void put(ResourcefulConfigElement element, Field field, Optional owner) { + REGISTRY.put(element, new Context(field, owner)); + } + + public static Optional get(ResourcefulConfigElement element) { + return Optional.ofNullable(REGISTRY.get(element)); + } +} diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/api/annotations/ShowIf.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/annotations/ShowIf.java new file mode 100644 index 0000000000..c56627f108 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/annotations/ShowIf.java @@ -0,0 +1,35 @@ +package com.github.mkram17.bazaarutils.config.util.api.annotations; + +import com.github.mkram17.bazaarutils.config.util.api.conditions.ConfigCondition; +import com.github.mkram17.bazaarutils.config.util.api.conditions.FieldEquals; +import com.github.mkram17.bazaarutils.config.util.api.conditions.MethodEquals; + +import java.lang.annotation.*; + +/** + * Declares one or more {@link ConfigCondition}s that must all evaluate to {@code true} + * for the annotated config field to be shown in the config screen. + * + *

When multiple conditions are listed, they are combined with AND semantics: + * all conditions must pass. For OR semantics, compose conditions explicitly inside + * a single named class using {@link ConfigCondition#or}: + * + *

Fail-open contract

+ * + *

A condition class that cannot be instantiated is treated as if it had returned + * {@code true} — the field is shown rather than silently hidden. + * + * @see ConfigCondition + * @see FieldEquals + * @see MethodEquals + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ShowIf { + + /** + * The condition classes to evaluate. All must return {@code true} for the + * annotated field to be shown (AND semantics). + */ + Class[] value(); +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/AdvancedConfigurationMode.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/AdvancedConfigurationMode.java new file mode 100644 index 0000000000..3bf7964f7e --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/AdvancedConfigurationMode.java @@ -0,0 +1,17 @@ +package com.github.mkram17.bazaarutils.config.util.api.conditions; + +import com.github.mkram17.bazaarutils.config.BUConfig; + +import java.util.Optional; + +/** + * A {@link ConfigCondition} that shows a field only when advanced configuration + * mode is enabled. Reads a static config flag; the {@code instance} parameter + * is ignored. + */ +public final class AdvancedConfigurationMode implements ConfigCondition { + @Override + public boolean shouldShow(Optional instance) { + return BUConfig.ADVANCED_CONFIGURATION_TOGGLE.get(); + } +} diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/AllOf.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/AllOf.java new file mode 100644 index 0000000000..fb78b4a037 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/AllOf.java @@ -0,0 +1,21 @@ +package com.github.mkram17.bazaarutils.config.util.api.conditions; + +import java.util.Optional; + +/** + * A {@link ConfigCondition} that shows a field only when ALL delegate conditions + * return {@code true}. Subclass with a no-arg constructor, override + * {@link #conditions()} — the result is a named class usable in {@code @ShowIf}. + * + *

Evaluation is delegated to {@link ConfigCondition#all}; an empty + * {@link #conditions()} array returns {@link ConfigCondition#TOP} (field shown). + */ +public abstract class AllOf implements ConfigCondition { + + protected abstract Class[] conditions(); + + @Override + public final boolean shouldShow(Optional instance) { + return ConfigCondition.all(conditions()).shouldShow(instance); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/AnyOf.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/AnyOf.java new file mode 100644 index 0000000000..8f1a52278d --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/AnyOf.java @@ -0,0 +1,21 @@ +package com.github.mkram17.bazaarutils.config.util.api.conditions; + +import java.util.Optional; + +/** + * A {@link ConfigCondition} that shows a field when ANY delegate condition + * returns {@code true}. Subclass with a no-arg constructor, override + * {@link #conditions()} — the result is a named class usable in {@code @ShowIf}. + * + *

Each delegate is instantiated via {@link ConfigCondition#of}, so fail-open + * behaviour is inherited from each condition's own contract. An empty + * {@link #conditions()} array returns {@link ConfigCondition#BOTTOM} (field hidden). + */ +public abstract class AnyOf implements ConfigCondition { + protected abstract Class[] conditions(); + + @Override + public final boolean shouldShow(Optional instance) { + return ConfigCondition.any(conditions()).shouldShow(instance); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/ConfigCondition.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/ConfigCondition.java new file mode 100644 index 0000000000..6d60f5e851 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/ConfigCondition.java @@ -0,0 +1,116 @@ +package com.github.mkram17.bazaarutils.config.util.api.conditions; + +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; + +import java.util.Optional; + +/** + * A boolean predicate over a config field's owner instance, determining whether + * the field should be visible in the config screen. + * + *

The {@code owner} passed to {@link #shouldShow} is: + * + *

    + *
  • {@code Optional.of(instance)} — for fields declared on a {@code @ConfigObject} + * instance (e.g. an input helper whose {@code fixedAmount} field is being evaluated) + *
  • {@code Optional.empty()} — for static fields (e.g. top-level {@code BUConfig} fields + * that have no owning instance at all) + *
+ * + *

Fail-open contract

+ * + *

All implementations should treat an absent owner, a type mismatch, or any + * reflection error as {@code true} — the field is shown. This prevents a broken + * condition from silently hiding configuration from the user. + * + * @see FieldEquals + * @see MethodEquals + * @see ShowIf + */ +@FunctionalInterface +public interface ConfigCondition { + /** ⊤ — identity for AND. A field is shown unless something hides it. */ + ConfigCondition TOP = instance -> true; + + /** ⊥ — identity for OR. A field is always hidden (useful as a base for folding). */ + ConfigCondition BOTTOM = instance -> false; + + /** + * Returns {@code true} if the annotated field should be shown in the config screen, + * given the owner instance of the field. + * + *

Implementations must never throw; they must return {@code true} (fail-open) + * in any error or indeterminate case. + * + * @param instance the owner of the annotated field, or {@code Optional.empty()} if + * the field is static and has no owning instance + */ + boolean shouldShow(Optional instance); + + /** + * Returns a condition that evaluates to {@code true} only when both this condition + * and {@code other} do. Short-circuits: {@code other} is not evaluated if this + * condition returns {@code false}. + */ + default ConfigCondition and(ConfigCondition other) { + return instance -> this.shouldShow(instance) && other.shouldShow(instance); + } + + /** + * Returns a condition that evaluates to {@code true} when either this condition + * or {@code other} does. Short-circuits: {@code other} is not evaluated if this + * condition returns {@code true}. + */ + default ConfigCondition or(ConfigCondition other) { + return instance -> this.shouldShow(instance) || other.shouldShow(instance); + } + + /** + * Returns a condition that evaluates to the logical negation of this condition. + */ + default ConfigCondition negate() { + return instance -> !this.shouldShow(instance); + } + + /** + * Instantiates the given condition class via its public no-arg constructor and + * returns the result. Fails open — returns a condition that always returns + * {@code true} — if instantiation fails for any reason. + * + *

This is the preferred way to obtain named condition instances inside composed + * conditions, so that the fail-open behaviour is consistent regardless of which + * class failed to load. + * + * @param cls a concrete {@code ConfigCondition} class with a public no-arg constructor + */ + static ConfigCondition of(Class cls) { + try { + return cls.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return instance -> true; + } + } + + /** + * foldMap over the AND-monoid. + * ConfigCondition.all() == TOP (empty fold returns identity, correct behaviour). + */ + @SafeVarargs + static ConfigCondition all(Class... classes) { + ConfigCondition acc = TOP; + + for (var cls : classes) acc = acc.and(ConfigCondition.of(cls)); + + return acc; + } + + /** foldMap over the OR-monoid. */ + @SafeVarargs + static ConfigCondition any(Class... classes) { + ConfigCondition acc = BOTTOM; + + for (var cls : classes) acc = acc.or(ConfigCondition.of(cls)); + + return acc; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/FieldEquals.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/FieldEquals.java new file mode 100644 index 0000000000..b61030f983 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/FieldEquals.java @@ -0,0 +1,81 @@ +package com.github.mkram17.bazaarutils.config.util.api.conditions; + +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; +import com.google.common.base.Preconditions; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Objects; +import java.util.Optional; + +/** + * A {@link ConfigCondition} that shows a field only when a named sibling field on + * the same owner instance holds a value equal to an expected value. + * + *

The class hierarchy of the owner is walked from most-derived to least-derived, + * so conditions defined on a supertype work correctly for subclass instances. + * + *

Fail-open contract

+ * + *

Returns {@code true} (show the field) when: + *

    + *
  • the owner is absent (the annotated field is static and has no owning instance) + *
  • no field with the given name exists anywhere in the class hierarchy + *
  • the field cannot be read due to an {@link IllegalAccessException} + *
+ * + * @param the type of the sibling field's value + * @see MethodEquals + */ +public class FieldEquals implements ConfigCondition { + + private final String fieldName; + private final T expected; + + /** + * Constructs a new {@code FieldEquals} condition. + * + * @param fieldName the exact name of the sibling field to read; must not be {@code null} + * @param expected the value the sibling field must equal; {@code null} is valid and + * checks that the field itself holds {@code null} + */ + public FieldEquals(String fieldName, T expected) { + this.fieldName = Preconditions.checkNotNull(fieldName, "fieldName"); + this.expected = expected; + } + + @Override + public final boolean shouldShow(Optional instance) { + return instance.map(it -> { + Field f = resolve(it.getClass()); + if (f == null) return true; + + try { + return Objects.equals(expected, f.get(it)); + } catch (IllegalAccessException e) { + return true; // fail-open per contract + } + }).orElse(true); + } + + @Nullable + private Field resolve(Class cls) { + for (Class c = cls; c != null; c = c.getSuperclass()) { + try { + Field f = c.getDeclaredField(fieldName); + f.setAccessible(true); + return f; + } catch (NoSuchFieldException ignored) {} + } + return null; + } + + @Nullable + private static Object safeGet(Field field, Object instance) { + try { + return field.get(instance); + } catch (IllegalAccessException e) { + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/MethodEquals.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/MethodEquals.java new file mode 100644 index 0000000000..6ed8507f42 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/MethodEquals.java @@ -0,0 +1,61 @@ +package com.github.mkram17.bazaarutils.config.util.api.conditions; + +import com.google.common.base.Preconditions; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + +/** + * A {@link ConfigCondition} that shows a field only when a typed accessor applied + * to the owner instance returns a value equal to an expected value. + * + *

Unlike {@link FieldEquals}, this class uses no reflection: the accessor is a + * plain {@link Function} — typically a method reference. + * + *

Fail-open contract

+ * + *

Returns {@code true} (show the field) when: + *

    + *
  • the owner is absent (the annotated field is static and has no owning instance) + *
  • the owner is present but is not an instance of {@code type} + *
+ * + * @param the expected runtime type of the owner instance + * @param the return type of the accessor + * @see FieldEquals + */ +public class MethodEquals implements ConfigCondition { + + private final Class type; + private final Function accessor; + private final T expected; + + /** + * Constructs a new {@code MethodEquals} condition. + * + * @param type the expected runtime type of the owner; owners that are not an + * instance of this type cause the condition to fail open; must not + * be {@code null} + * @param accessor a function that extracts the comparable value from a typed owner + * instance; typically a method reference; must not be {@code null} + * and must not throw + * @param expected the value the accessor must return; {@code null} is valid and + * checks that the accessor itself returns {@code null} + */ + public MethodEquals(Class type, Function accessor, T expected) { + this.type = Preconditions.checkNotNull(type, "type"); + this.accessor = Preconditions.checkNotNull(accessor, "accessor"); + this.expected = expected; + } + + @Override + public final boolean shouldShow(Optional instance) { + return instance + .filter(type::isInstance) + .map(type::cast) + .map(accessor) + .map(v -> Objects.equals(expected, v)) + .orElse(true); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/Not.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/Not.java new file mode 100644 index 0000000000..dfd5fc58a3 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/api/conditions/Not.java @@ -0,0 +1,17 @@ +package com.github.mkram17.bazaarutils.config.util.api.conditions; + +import java.util.Optional; + +/** + * A {@link ConfigCondition} that inverts a delegate condition. + * Subclass with a no-arg constructor, override {@link #condition()} — + * the result is a named class usable in {@code @ShowIf}. + */ +public abstract class Not implements ConfigCondition { + protected abstract Class condition(); + + @Override + public final boolean shouldShow(Optional instance) { + return !ConfigCondition.of(condition()).shouldShow(instance); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java index f354222171..c50eddbeba 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java @@ -1,7 +1,9 @@ package com.github.mkram17.bazaarutils.features.gui.buttons.inputhelper.amount; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; import com.github.mkram17.bazaarutils.config.util.api.SlotProviders; import com.github.mkram17.bazaarutils.config.util.api.annotations.ContainerSlot; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.utils.bazaar.SignInputHelper; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenMatcher; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenType; @@ -37,6 +39,7 @@ public class BuyOrderAmountHelper extends SignInputHelper.TransactionAmount impl translation = "bazaarutils.config.buttons.button.container.item_id.hint" ) @ConfigOption.Renderer("bazaarutils:item") + @ShowIf(AdvancedConfigurationMode.class) public String itemId = "minecraft:green_stained_glass_pane"; @ConfigEntry( @@ -50,6 +53,7 @@ public class BuyOrderAmountHelper extends SignInputHelper.TransactionAmount impl @ContainerSlot(rows = 4, cols = 9, provider = "bazaar:buy_order_amount") @ConfigOption.Range(min = 0, max = 35) @ConfigOption.Renderer("bazaarutils:slot") + @ShowIf(AdvancedConfigurationMode.class) public int slotIndex; @ConfigEntry( @@ -75,6 +79,7 @@ public class BuyOrderAmountHelper extends SignInputHelper.TransactionAmount impl value = "Amount used for FIXED input strategy.", translation = "bazaarutils.config.buttons.button.container.fixed_amount.hint" ) + @ShowIf(SignInputHelper.TransactionAmount.WhenFixedStrategy.class) public int fixedAmount = 1; public TransactionType transactionType = TransactionType.of(TransactionType.Side.BUY, TransactionType.Method.ORDER); diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java index 8e9c73078c..23b5cfdf64 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java @@ -1,7 +1,9 @@ package com.github.mkram17.bazaarutils.features.gui.buttons.inputhelper.amount; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; import com.github.mkram17.bazaarutils.config.util.api.SlotProviders; import com.github.mkram17.bazaarutils.config.util.api.annotations.ContainerSlot; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.utils.bazaar.SignInputHelper; import com.github.mkram17.bazaarutils.utils.bazaar.data.BazaarDataUtil; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenMatcher; @@ -38,6 +40,7 @@ public class InstantBuyAmountHelper extends SignInputHelper.TransactionAmount im translation = "bazaarutils.config.buttons.button.container.item_id.hint" ) @ConfigOption.Renderer("bazaarutils:item") + @ShowIf(AdvancedConfigurationMode.class) public String itemId = "minecraft:green_stained_glass_pane"; @ConfigEntry( @@ -51,6 +54,7 @@ public class InstantBuyAmountHelper extends SignInputHelper.TransactionAmount im @ContainerSlot(rows = 4, cols = 9, provider = "bazaar:instant_buy_amount") @ConfigOption.Range(min = 0, max = 35) @ConfigOption.Renderer("bazaarutils:slot") + @ShowIf(AdvancedConfigurationMode.class) public int slotIndex; @ConfigEntry( @@ -76,6 +80,7 @@ public class InstantBuyAmountHelper extends SignInputHelper.TransactionAmount im value = "Amount used for FIXED input strategy.", translation = "bazaarutils.config.buttons.button.container.fixed_amount.hint" ) + @ShowIf(SignInputHelper.TransactionAmount.WhenFixedStrategy.class) public int fixedAmount = 1; public TransactionType transactionType = TransactionType.of(TransactionType.Side.BUY, TransactionType.Method.INSTANT); diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java index 426a4f9dab..2fe64367cb 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java @@ -1,7 +1,9 @@ package com.github.mkram17.bazaarutils.features.gui.buttons.inputhelper.amount; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; import com.github.mkram17.bazaarutils.config.util.api.SlotProviders; import com.github.mkram17.bazaarutils.config.util.api.annotations.ContainerSlot; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.utils.bazaar.SignInputHelper; import com.github.mkram17.bazaarutils.utils.bazaar.data.BazaarDataUtil; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenMatcher; @@ -36,6 +38,7 @@ public class SellOfferAmountHelper extends SignInputHelper.TransactionAmount imp translation = "bazaarutils.config.buttons.button.container.item_id.hint" ) @ConfigOption.Renderer("bazaarutils:item") + @ShowIf(AdvancedConfigurationMode.class) public String itemId = "minecraft:green_stained_glass_pane"; @ConfigEntry( @@ -49,6 +52,7 @@ public class SellOfferAmountHelper extends SignInputHelper.TransactionAmount imp @ContainerSlot(rows = 4, cols = 9, provider = "bazaar:sell_offer_amount") @ConfigOption.Range(min = 0, max = 35) @ConfigOption.Renderer("bazaarutils:slot") + @ShowIf(AdvancedConfigurationMode.class) public int slotIndex; @ConfigEntry( @@ -74,6 +78,7 @@ public class SellOfferAmountHelper extends SignInputHelper.TransactionAmount imp value = "Amount used for FIXED input strategy.", translation = "bazaarutils.config.buttons.button.container.fixed_amount.hint" ) + @ShowIf(SignInputHelper.TransactionAmount.WhenFixedStrategy.class) public int fixedAmount = 1; public TransactionType transactionType = TransactionType.of(TransactionType.Side.SELL, TransactionType.Method.ORDER); diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/BuyOrderPriceHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/BuyOrderPriceHelper.java index 1dac3e8ae0..0724d52fd7 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/BuyOrderPriceHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/BuyOrderPriceHelper.java @@ -1,5 +1,7 @@ package com.github.mkram17.bazaarutils.features.gui.buttons.inputhelper.price; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.config.util.api.SlotProviders; import com.github.mkram17.bazaarutils.config.util.api.annotations.ContainerSlot; import com.github.mkram17.bazaarutils.utils.bazaar.SignInputHelper; @@ -33,6 +35,7 @@ public class BuyOrderPriceHelper extends SignInputHelper.TransactionCost impleme translation = "bazaarutils.config.buttons.button.container.item_id.hint" ) @ConfigOption.Renderer("bazaarutils:item") + @ShowIf(AdvancedConfigurationMode.class) public String itemId = "minecraft:green_stained_glass_pane"; @ConfigEntry( @@ -46,6 +49,7 @@ public class BuyOrderPriceHelper extends SignInputHelper.TransactionCost impleme @ContainerSlot(rows = 4, cols = 9, provider = "bazaar:buy_order_price") @ConfigOption.Range(min = 0, max = 35) @ConfigOption.Renderer("bazaarutils:slot") + @ShowIf(AdvancedConfigurationMode.class) public int slotIndex; @ConfigEntry( diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/FlipOrderPriceHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/FlipOrderPriceHelper.java index ab0ca1c78f..cb0956cbe3 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/FlipOrderPriceHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/FlipOrderPriceHelper.java @@ -1,5 +1,7 @@ package com.github.mkram17.bazaarutils.features.gui.buttons.inputhelper.price; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.config.util.api.SlotProviders; import com.github.mkram17.bazaarutils.config.util.api.annotations.ContainerSlot; import com.github.mkram17.bazaarutils.utils.bazaar.SignInputHelper; @@ -33,6 +35,7 @@ public class FlipOrderPriceHelper extends SignInputHelper.TransactionFlip implem translation = "bazaarutils.config.buttons.button.container.item_id.hint" ) @ConfigOption.Renderer("bazaarutils:item") + @ShowIf(AdvancedConfigurationMode.class) public String itemId = "minecraft:green_stained_glass_pane"; @ConfigEntry( @@ -46,6 +49,7 @@ public class FlipOrderPriceHelper extends SignInputHelper.TransactionFlip implem @ContainerSlot(rows = 4, cols = 9, provider = "bazaar:flip_filled_buy_order") @ConfigOption.Range(min = 0, max = 35) @ConfigOption.Renderer("bazaarutils:slot") + @ShowIf(AdvancedConfigurationMode.class) public int slotIndex; @ConfigEntry( diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/SellOfferPriceHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/SellOfferPriceHelper.java index 03fc8831e2..27f91d650a 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/SellOfferPriceHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/SellOfferPriceHelper.java @@ -1,5 +1,7 @@ package com.github.mkram17.bazaarutils.features.gui.buttons.inputhelper.price; +import com.github.mkram17.bazaarutils.config.util.api.conditions.AdvancedConfigurationMode; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; import com.github.mkram17.bazaarutils.config.util.api.SlotProviders; import com.github.mkram17.bazaarutils.config.util.api.annotations.ContainerSlot; import com.github.mkram17.bazaarutils.utils.bazaar.SignInputHelper; @@ -33,6 +35,7 @@ public class SellOfferPriceHelper extends SignInputHelper.TransactionCost implem translation = "bazaarutils.config.buttons.button.container.item_id.hint" ) @ConfigOption.Renderer("bazaarutils:item") + @ShowIf(AdvancedConfigurationMode.class) public String itemId = "minecraft:green_stained_glass_pane"; @ConfigEntry( @@ -46,6 +49,7 @@ public class SellOfferPriceHelper extends SignInputHelper.TransactionCost implem @ContainerSlot(rows = 4, cols = 9, provider = "bazaar:sell_offer_price") @ConfigOption.Range(min = 0, max = 35) @ConfigOption.Renderer("bazaarutils:slot") + @ShowIf(AdvancedConfigurationMode.class) public int slotIndex; @ConfigEntry( diff --git a/src/main/java/com/github/mkram17/bazaarutils/mixin/AdvancedOptionsMixin.java b/src/main/java/com/github/mkram17/bazaarutils/mixin/AdvancedOptionsMixin.java new file mode 100644 index 0000000000..b414ab6ceb --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/mixin/AdvancedOptionsMixin.java @@ -0,0 +1,69 @@ +package com.github.mkram17.bazaarutils.mixin; + +import com.github.mkram17.bazaarutils.config.util.ConfigElementFieldStore; +import com.github.mkram17.bazaarutils.config.util.api.annotations.ShowIf; +import com.github.mkram17.bazaarutils.config.util.api.conditions.ConfigCondition; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.teamresourceful.resourcefulconfig.api.types.ResourcefulConfigElement; +import com.teamresourceful.resourcefulconfig.api.types.ResourcefulConfigButton; +import com.teamresourceful.resourcefulconfig.api.types.elements.ResourcefulConfigEntryElement; +import com.teamresourceful.resourcefulconfig.api.types.elements.ResourcefulConfigSeparatorElement; +import com.teamresourceful.resourcefulconfig.api.types.entries.ResourcefulConfigFieldBackedValueEntry; +import com.teamresourceful.resourcefulconfig.client.components.options.Options; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.lang.reflect.Field; +import java.util.Optional; + +@Mixin(value = Options.class, remap = false) +public class AdvancedOptionsMixin { + @Unique + private static final LoadingCache, ConfigCondition> CONDITION_CACHE = + CacheBuilder.newBuilder().build(CacheLoader.from(cls -> { + try { + return cls.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return owner -> true; + } + })); + + @Redirect( + method = "populateOptions", + at = @At( + value = "INVOKE", + target = "Lcom/teamresourceful/resourcefulconfig/api/types/ResourcefulConfigElement;isHidden()Z" + ) + ) + private static boolean checkHidden(ResourcefulConfigElement element) { + if (element.isHidden()) return true; + + if (element instanceof ResourcefulConfigSeparatorElement || element instanceof ResourcefulConfigButton) { + return ConfigElementFieldStore.get(element) + .filter(ctx -> ctx.field().isAnnotationPresent(ShowIf.class)) + .map(ctx -> hiddenByCondition(ctx.field(), ctx.owner())) + .orElse(false); + } + + if (!(element instanceof ResourcefulConfigEntryElement entry)) return false; + if (!(entry.entry() instanceof ResourcefulConfigFieldBackedValueEntry backed)) return false; + + return hiddenByCondition(backed.field(), Optional.ofNullable(backed.instance())); + } + + @Unique + private static boolean hiddenByCondition(Field field, Optional owner) { + ShowIf ann = field.getAnnotation(ShowIf.class); + if (ann == null) return false; + + for (Class cls : ann.value()) { + if (!CONDITION_CACHE.getUnchecked(cls).shouldShow(owner)) return true; + } + + return false; + } +} diff --git a/src/main/java/com/github/mkram17/bazaarutils/mixin/JavaConfigParserMixin.java b/src/main/java/com/github/mkram17/bazaarutils/mixin/JavaConfigParserMixin.java new file mode 100644 index 0000000000..68edfd0841 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/mixin/JavaConfigParserMixin.java @@ -0,0 +1,90 @@ +package com.github.mkram17.bazaarutils.mixin; + +import com.github.mkram17.bazaarutils.config.util.ConfigElementFieldStore; +import com.teamresourceful.resourcefulconfig.api.types.ResourcefulConfigElement; +import com.teamresourceful.resourcefulconfig.common.loader.JavaConfigParser; +import com.teamresourceful.resourcefulconfig.common.loader.elements.ParsedButtonElement; +import com.teamresourceful.resourcefulconfig.common.loader.elements.ParsedSeparator; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Optional; + +@Mixin(value = JavaConfigParser.class, remap = false) +public class JavaConfigParserMixin { + + /** + * Holds the current {@code @ConfigObject} owner instance for the duration of + * {@code populateObjectEntryElements}. Empty outside that scope (top-level static fields). + */ + @Unique + private static final ThreadLocal OBJECT_SCOPE = new ThreadLocal<>(); + + @Inject(method = "populateObjectEntryElements", at = @At("HEAD")) + private static void enterScope(Object instance, List elements, CallbackInfo ci) { + OBJECT_SCOPE.set(instance); + } + + @Inject(method = "populateObjectEntryElements", at = @At("RETURN")) + private static void exitScope(Object instance, List elements, CallbackInfo ci) { + OBJECT_SCOPE.remove(); + } + + /** + * Intercepts {@code ParsedSeparator.of} inside {@code populateObjectEntryElements} + * (instance fields of a {@code @ConfigObject}). Owner comes from {@link #OBJECT_SCOPE}. + */ + @Redirect( + method = "populateObjectEntryElements", + at = @At( + value = "INVOKE", + target = "Lcom/teamresourceful/resourcefulconfig/common/loader/elements/ParsedSeparator;of(Ljava/lang/reflect/Field;)Lcom/teamresourceful/resourcefulconfig/common/loader/elements/ParsedSeparator;" + ) + ) + private static ParsedSeparator captureObjectSeparator(Field field) { + ParsedSeparator separator = ParsedSeparator.of(field); + + ConfigElementFieldStore.put(separator, field, Optional.ofNullable(OBJECT_SCOPE.get())); + + return separator; + } + + /** + * Intercepts {@code ParsedSeparator.of} inside the top-level/category + * {@code populateEntries}. These are always static config fields — owner is empty. + */ + @Redirect( + method = "populateEntries(Ljava/lang/Class;Lcom/teamresourceful/resourcefulconfig/api/types/ResourcefulConfig;[Ljava/lang/Class;)Lcom/teamresourceful/resourcefulconfig/api/types/ResourcefulConfig;", + at = @At( + value = "INVOKE", + target = "Lcom/teamresourceful/resourcefulconfig/common/loader/elements/ParsedSeparator;of(Ljava/lang/reflect/Field;)Lcom/teamresourceful/resourcefulconfig/common/loader/elements/ParsedSeparator;" + ) + ) + private static ParsedSeparator captureStaticSeparator(Field field) { + ParsedSeparator separator = ParsedSeparator.of(field); + + ConfigElementFieldStore.put(separator, field, Optional.empty()); + + return separator; + } + + /** Config buttons are static fields, and their parsed elements do not retain the field. */ + @Redirect( + method = "populateEntries(Ljava/lang/Class;Lcom/teamresourceful/resourcefulconfig/api/types/ResourcefulConfig;[Ljava/lang/Class;)Lcom/teamresourceful/resourcefulconfig/api/types/ResourcefulConfig;", + at = @At( + value = "INVOKE", + target = "Lcom/teamresourceful/resourcefulconfig/common/loader/elements/ParsedButtonElement;of(Ljava/lang/reflect/Field;)Lcom/teamresourceful/resourcefulconfig/common/loader/elements/ParsedButtonElement;" + ) + ) + private static ParsedButtonElement captureStaticButton(Field field) { + ParsedButtonElement button = ParsedButtonElement.of(field); + ConfigElementFieldStore.put(button, field, Optional.empty()); + return button; + } +} diff --git a/src/main/java/com/github/mkram17/bazaarutils/mixin/ObservableConfigRefreshMixin.java b/src/main/java/com/github/mkram17/bazaarutils/mixin/ObservableConfigRefreshMixin.java new file mode 100644 index 0000000000..8ae7f3c808 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/mixin/ObservableConfigRefreshMixin.java @@ -0,0 +1,20 @@ +package com.github.mkram17.bazaarutils.mixin; + +import com.teamresourceful.resourcefulconfig.api.types.entries.Observable; +import com.teamresourceful.resourcefulconfig.client.ConfigScreen; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = Observable.class, remap = false) +public class ObservableConfigRefreshMixin { + + @Inject(method = "accept", at = @At("TAIL")) + private void bazaarutils$onAccept(Object value, CallbackInfo ci) { + if (Minecraft.getInstance().gui.screen() instanceof ConfigScreen configScreen) { + configScreen.updateOptions(); + } + } +} diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java index 9622bf848d..bdf860e37a 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java @@ -1,5 +1,7 @@ package com.github.mkram17.bazaarutils.utils.bazaar; +import com.github.mkram17.bazaarutils.config.util.api.conditions.MethodEquals; +import com.github.mkram17.bazaarutils.data.stored.UserOrdersStorage; import com.github.mkram17.bazaarutils.events.minecraft.ContainerLoadedEvent; import com.github.mkram17.bazaarutils.utils.Result; import com.github.mkram17.bazaarutils.utils.bazaar.gui.layouts.ProductPageLayout; @@ -195,6 +197,12 @@ public enum AmountStrategy { */ protected abstract AmountStrategy getAmountStrategy(); + public static final class WhenFixedStrategy extends MethodEquals { + public WhenFixedStrategy() { + super(TransactionAmount.class, TransactionAmount::getAmountStrategy, AmountStrategy.FIXED); + } + } + @Override protected Optional makeState(ContainerLoadedEvent event) { Container container = event.getContainer(); diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/update/UpdateUtil.java b/src/main/java/com/github/mkram17/bazaarutils/utils/update/UpdateUtil.java index 7579359532..c9dbb6c75c 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/update/UpdateUtil.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/update/UpdateUtil.java @@ -1,7 +1,7 @@ package com.github.mkram17.bazaarutils.utils.update; import com.github.mkram17.bazaarutils.BazaarUtils; -import com.github.mkram17.bazaarutils.config.features.AdvancedConfig; +import com.github.mkram17.bazaarutils.config.BUConfig; import com.github.mkram17.bazaarutils.config.hidden.MetadataConfig; import com.github.mkram17.bazaarutils.config.util.ConfigUtil; import com.github.mkram17.bazaarutils.utils.PlayerActionUtil; @@ -103,7 +103,7 @@ public static void checkForUpdates() { return CompletableFuture.completedFuture(null); } - if (AdvancedConfig.AUTO_UPDATE_TOGGLE) { + if (BUConfig.AUTOMATIC_UPDATES_TOGGLE) { return update.launchUpdate().thenRun(() -> PlayerActionUtil.notifyAll("Update downloaded! Restart to apply.")); } else { PlayerActionUtil.notifyAll( diff --git a/src/main/resources/assets/bazaarutils/lang/en_us.json5 b/src/main/resources/assets/bazaarutils/lang/en_us.json5 index 850bb8a2e4..4ec512916b 100644 --- a/src/main/resources/assets/bazaarutils/lang/en_us.json5 +++ b/src/main/resources/assets/bazaarutils/lang/en_us.json5 @@ -93,6 +93,16 @@ "separator.introductory.label": "Welcome to Bazaar Utils!", "separator.introductory.hint": "Thank you for installing Bazaar Utils! This mod provides quality of life features for the Hypixel Bazaar.", + "advanced_configuration_mode.{}": { + "label": "Advanced Configuration Mode", + "hint": "Certain feature customization is hidden away from a normal configuration of the mod as they're options generally of no interest. Here you can toggle to see and configure them as well." + }, + + "automatic_updates.{}": { + "label": "Automatic Updates", + "hint": "Automatically update the mod when an update is found." + }, + "bazaar_flipper_account_upgrade.{}": { "label": "Bazaar Flipper Account Upgrade", "hint": [ @@ -466,15 +476,6 @@ } }, - "advanced.{}": { - "category.label": "Advanced Settings", - "category.hint": "Advanced configurations for routines & features of the mod", - "auto_update.{}": { - "label": "Auto Update", - "hint": "Automatically update the mod when an update is found." - } - }, - "developer.{}": { "category.label": "Developer Config", "category.hint": "Developer configurations & toggleable tools of the mod", diff --git a/src/main/resources/bazaarutils.mixins.json b/src/main/resources/bazaarutils.mixins.json index 369c514c9e..3b391f1e2a 100644 --- a/src/main/resources/bazaarutils.mixins.json +++ b/src/main/resources/bazaarutils.mixins.json @@ -15,5 +15,10 @@ "MixinAbstractContainerScreen", "MixinSignEditScreen", "AccessorSkyBlockBazaarReply" + ], + "mixins": [ + "AdvancedOptionsMixin", + "JavaConfigParserMixin", + "ObservableConfigRefreshMixin" ] } \ No newline at end of file