diff --git a/build.gradle b/build.gradle index 042c37bebc0..e91b6a92bff 100644 --- a/build.gradle +++ b/build.gradle @@ -295,7 +295,7 @@ spotless { // The third and remaining stems are optional, allowing matching lines with only two stems (in this case "skyblocker.config"), and separating it from other keys that start with "skyblocker.config.[whatever]". /^( "skyblocker\.config(?:\.([^."]++)(?:\.[^"]++)?)?": "(?:[^\\"]++|\\.)*+",)$\n^(?= "skyblocker\.config\.(?!\2[."]))/, '$1\n\n' - final List excludeCategories = ["debug", "eventNotifications", "quickNav", "shortcutToKeybindsSettings", "title"] + final List excludeCategories = ["commands", "debug", "eventNotifications", "quickNav", "shortcutToKeybindsSettings", "title"] final String excludeCategoriesLookahead = /(?!(?:${excludeCategories.join('|')})(?=[."]))/ replaceRegex \ "Separate different fouth stems if the first three stems are skyblocker.config.[configCategory]", diff --git a/src/main/java/de/hysky/skyblocker/DisableAll.java b/src/main/java/de/hysky/skyblocker/DisableAll.java index 8ae78718b12..27570f7fbdb 100644 --- a/src/main/java/de/hysky/skyblocker/DisableAll.java +++ b/src/main/java/de/hysky/skyblocker/DisableAll.java @@ -23,7 +23,6 @@ */ public class DisableAll { private static final Logger LOGGER = LogUtils.getLogger(); - private static final String CONFIGS_PACKAGE = "de.hysky.skyblocker.config.configs"; @Init public static void init() { @@ -95,23 +94,9 @@ private static void disableBooleans(Object target) throws IllegalAccessException m.put(entry.getKey(), Boolean.FALSE); } } - } else if (value != null && isConfigClass(type)) { + } else if (value != null && SkyblockerConfigManager.isConfigClass(type)) { disableBooleans(value); } } } - - /** - * Returns {@code true} if the given class represents one of our config - * classes. This prevents {@link #disableBooleans(Object)} from touching - * unrelated objects from other mods. - */ - private static boolean isConfigClass(Class clazz) { - return !clazz.isPrimitive() - && !clazz.isEnum() - && !clazz.isRecord() - && !clazz.equals(String.class) - && !Number.class.isAssignableFrom(clazz) - && clazz.getPackageName().startsWith(CONFIGS_PACKAGE); - } } diff --git a/src/main/java/de/hysky/skyblocker/config/ConfigCommands.java b/src/main/java/de/hysky/skyblocker/config/ConfigCommands.java new file mode 100644 index 00000000000..657f1e92233 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/ConfigCommands.java @@ -0,0 +1,77 @@ +package de.hysky.skyblocker.config; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import de.hysky.skyblocker.utils.Constants; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +public class ConfigCommands { + static void registerConfigEntries(LiteralArgumentBuilder builder) { + try { + registerConfigEntries(builder, SkyblockerConfigManager.get()); + } catch (Exception e) { + SkyblockerConfigManager.LOGGER.error("[Skyblocker Config Manager] Failed to register config entries command!", e); + } + } + + private static LiteralArgumentBuilder registerConfigEntries(LiteralArgumentBuilder builder, Object object) throws IllegalAccessException { + for (Field field : object.getClass().getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + field.setAccessible(true); + + Class type = field.getType(); + String name = field.getName(); + Object value = field.get(object); + + if (type == boolean.class) { + builder.then(registerBooleanConfigEntry(field, object, name)); + } else if (value != null && SkyblockerConfigManager.isConfigClass(type)) { + builder.then(registerConfigEntries(literal(name), value)); + } + } + + return builder; + } + + private static LiteralArgumentBuilder registerBooleanConfigEntry(Field field, Object object, String name) { + return literal(name).then(argument("value", BoolArgumentType.bool()).executes(context -> { + SkyblockerConfigManager.update(config -> { + try { + boolean value = BoolArgumentType.getBool(context, "value"); + field.setBoolean(object, value); + context.getSource().sendFeedback(Constants.PREFIX.get().append(Component.translatable("skyblocker.config.commands.set", name, value).withStyle(ChatFormatting.GREEN))); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + return Command.SINGLE_SUCCESS; + })).then(literal("toggle").executes(context -> { + SkyblockerConfigManager.update(config -> { + try { + boolean toggled = !field.getBoolean(object); + field.setBoolean(object, toggled); + context.getSource().sendFeedback(Constants.PREFIX.get().append(Component.translatable("skyblocker.config.commands.set", name, toggled).withStyle(ChatFormatting.GREEN))); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + return Command.SINGLE_SUCCESS; + })).executes(context -> { + try { + context.getSource().sendFeedback(Constants.PREFIX.get().append(Component.translatable("skyblocker.config.commands.query", name, field.getBoolean(object)).withStyle(ChatFormatting.GREEN))); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + return Command.SINGLE_SUCCESS; + }); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/ConfigNullFieldsFix.java b/src/main/java/de/hysky/skyblocker/config/ConfigNullFieldsFix.java index ab8709d73b3..54c8d57f35e 100644 --- a/src/main/java/de/hysky/skyblocker/config/ConfigNullFieldsFix.java +++ b/src/main/java/de/hysky/skyblocker/config/ConfigNullFieldsFix.java @@ -1,8 +1,6 @@ package de.hysky.skyblocker.config; import java.lang.reflect.Field; -import java.util.Collection; -import java.util.Map; import org.slf4j.Logger; @@ -16,49 +14,34 @@ */ public class ConfigNullFieldsFix { private static final Logger LOGGER = LogUtils.getLogger(); - private static final String CONFIGS_PACKAGE = "de.hysky.skyblocker.config.configs"; - @SuppressWarnings("removal") public static void init() { - SkyblockerConfig current = SkyblockerConfigManager.get(); - SkyblockerConfig clean = new SkyblockerConfig(); - - try { - fixNullFields(current, clean); - SkyblockerConfigManager.save(); - } catch (Exception e) { - LOGGER.error("[Skyblocker Config Null Fields Fixer] Failed to ensure that the config has no null fields! You may encounter crashes :(", e); - } + SkyblockerConfigManager.update(config -> { + try { + fixNullFields(config, new SkyblockerConfig()); + } catch (Exception e) { + LOGGER.error("[Skyblocker Config Null Fields Fixer] Failed to ensure that the config has no null fields! You may encounter crashes :(", e); + } + }); } /** * Traverse through every config field to ensure that is isn't null, if it is then reset the value. */ - private static void fixNullFields(Object target, Object source) throws Exception { - for (Field field : target.getClass().getDeclaredFields()) { + private static void fixNullFields(Object config, Object defaultConfig) throws Exception { + for (Field field : config.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(SerialEntry.class)) { field.setAccessible(true); - Object targetValue = field.get(target); - Object sourceValue = field.get(source); + Object configValue = field.get(config); + Object defaultValue = field.get(defaultConfig); - if (targetValue == null && sourceValue != null) { - field.set(target, sourceValue); - } else if (targetValue != null && sourceValue != null && isFixable(field.getType())) { - fixNullFields(targetValue, sourceValue); + if (configValue == null && defaultValue != null) { + field.set(config, defaultValue); + } else if (configValue != null && defaultValue != null && SkyblockerConfigManager.isConfigClass(field.getType())) { + fixNullFields(configValue, defaultValue); } } } } - - private static boolean isFixable(Class clazz) { - return !clazz.isPrimitive() - && !clazz.isEnum() - && !clazz.isRecord() - && !clazz.equals(String.class) - && !Number.class.isAssignableFrom(clazz) - && !Map.class.isAssignableFrom(clazz) - && !Collection.class.isAssignableFrom(clazz) - && clazz.getPackageName().startsWith(CONFIGS_PACKAGE); - } } diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java index 7b2f647f87e..404472f7c05 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java @@ -1,8 +1,8 @@ package de.hysky.skyblocker.config; -import com.mojang.brigadier.arguments.StringArgumentType; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.logging.LogUtils; import de.hysky.skyblocker.SkyblockerMod; @@ -42,22 +42,25 @@ import net.minecraft.network.chat.Component; import org.apache.commons.lang3.function.Consumers; import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; import java.io.BufferedReader; import java.io.BufferedWriter; import java.lang.StackWalker.Option; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collection; +import java.util.Map; import java.util.function.Consumer; import java.util.function.UnaryOperator; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; -import org.slf4j.Logger; public class SkyblockerConfigManager { public static final int CONFIG_VERSION = 6; - private static final Logger LOGGER = LogUtils.getLogger(); + static final Logger LOGGER = LogUtils.getLogger(); + private static final String CONFIGS_PACKAGE = "de.hysky.skyblocker.config.configs"; private static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir(); private static final Path CONFIG_FILE = CONFIG_DIR.resolve("skyblocker.json"); private static final ConfigManager CONFIG_MANAGER = ConfigManager.create(SkyblockerConfig.class, CONFIG_FILE, UnaryOperator.identity()); @@ -103,7 +106,7 @@ public static void update(Consumer action) { CONFIG_MANAGER.save(); } - public static Screen createGUI(Screen parent) { + public static Screen createGUI(@Nullable Screen parent) { return createGUI(parent, ""); } @@ -150,8 +153,27 @@ public static void reload() { * @return the command builder */ private static LiteralArgumentBuilder configLiteral(String name) { - return literal(name).executes(Scheduler.queueOpenScreenCommand(() -> createGUI(null))) + LiteralArgumentBuilder builder = literal(name).executes(Scheduler.queueOpenScreenCommand(() -> createGUI(null))) .then(argument("option", StringArgumentType.greedyString()).executes((ctx) -> Scheduler.queueOpenScreen(createGUI(null, ctx.getArgument("option", String.class))))); + ConfigCommands.registerConfigEntries(builder); + return builder; + } + + /** + * Returns {@code true} if the given class represents one of our config + * classes. This prevents {@link de.hysky.skyblocker.DisableAll#disableBooleans(Object)} from touching + * unrelated objects from other mods. + */ + @SuppressWarnings("JavadocReference") + public static boolean isConfigClass(Class clazz) { + return !clazz.isPrimitive() + && !clazz.isEnum() + && !clazz.isRecord() + && !clazz.equals(String.class) + && !Number.class.isAssignableFrom(clazz) + && !Map.class.isAssignableFrom(clazz) + && !Collection.class.isAssignableFrom(clazz) + && clazz.getPackageName().startsWith(CONFIGS_PACKAGE); } public static void dataFix(Path configDir, Path backupDir) { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/HotbarSlotLock.java b/src/main/java/de/hysky/skyblocker/skyblock/item/HotbarSlotLock.java index 1a207443739..3c76c79367c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/HotbarSlotLock.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/HotbarSlotLock.java @@ -28,11 +28,12 @@ public static boolean isLocked(int slot) { public static void handleInputEvents(LocalPlayer player) { while (hotbarSlotLock.consumeClick()) { - List lockedSlots = SkyblockerConfigManager.get().general.lockedSlots; int selected = player.getInventory().getSelectedSlot(); - if (!isLocked(player.getInventory().getSelectedSlot())) lockedSlots.add(selected); - else lockedSlots.remove(Integer.valueOf(selected)); - SkyblockerConfigManager.save(); + SkyblockerConfigManager.update(config -> { + List lockedSlots = config.general.lockedSlots; + if (!lockedSlots.contains(selected)) lockedSlots.add(selected); + else lockedSlots.remove(Integer.valueOf(selected)); + }); } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java index cb6b7a8357f..bb91f5181b9 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java @@ -65,17 +65,17 @@ private static int protectMyItem(FabricClientCommandSource source) { String itemUuid = heldItem.getUuid(); if (!itemUuid.isEmpty()) { - ObjectOpenHashSet protectedItems = SkyblockerConfigManager.get().general.protectedItems; - - if (!protectedItems.contains(itemUuid)) { - protectedItems.add(itemUuid); - SkyblockerConfigManager.save(); - source.sendFeedback(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.added", heldItem.getHoverName()))); - } else { - protectedItems.remove(itemUuid); - SkyblockerConfigManager.save(); - source.sendFeedback(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.removed", heldItem.getHoverName()))); - } + SkyblockerConfigManager.update(config -> { + ObjectOpenHashSet protectedItems = config.general.protectedItems; + + if (!protectedItems.contains(itemUuid)) { + protectedItems.add(itemUuid); + source.sendFeedback(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.added", heldItem.getHoverName()))); + } else { + protectedItems.remove(itemUuid); + source.sendFeedback(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.removed", heldItem.getHoverName()))); + } + }); } else { source.sendFeedback(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.noItemUuid"))); } @@ -105,21 +105,21 @@ public static void handleKeyPressed(ItemStack heldItem) { String itemUuid = heldItem.getUuid(); if (!itemUuid.isEmpty()) { - ObjectOpenHashSet protectedItems = SkyblockerConfigManager.get().general.protectedItems; + SkyblockerConfigManager.update(config -> { + ObjectOpenHashSet protectedItems = config.general.protectedItems; - if (!protectedItems.contains(itemUuid)) { - protectedItems.add(itemUuid); - SkyblockerConfigManager.save(); - if (notifyConfiguration) { - playerEntity.displayClientMessage(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.added", heldItem.getHoverName())), false); - } - } else { - protectedItems.remove(itemUuid); - SkyblockerConfigManager.save(); - if (notifyConfiguration) { - playerEntity.displayClientMessage(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.removed", heldItem.getHoverName())), false); + if (!protectedItems.contains(itemUuid)) { + protectedItems.add(itemUuid); + if (notifyConfiguration) { + playerEntity.displayClientMessage(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.added", heldItem.getHoverName())), false); + } + } else { + protectedItems.remove(itemUuid); + if (notifyConfiguration) { + playerEntity.displayClientMessage(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.removed", heldItem.getHoverName())), false); + } } - } + }); } else { playerEntity.displayClientMessage(Constants.PREFIX.get().append(Component.translatable("skyblocker.itemProtection.noItemUuid")), false); } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index c3d22daa44f..ccf8922d466 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -147,6 +147,9 @@ "skyblocker.config.chat.skyblockXpMessages": "SkyBlock XP Messages", "skyblocker.config.chat.skyblockXpMessages.@Tooltip": "Notifies you in the chat when you gain SkyBlock XP.", + "skyblocker.config.commands.query": "Option '%s' is currently set to %s", + "skyblocker.config.commands.set": "Option '%s' is now set to %s", + "skyblocker.config.crimsonIsle": "Crimson Isle", "skyblocker.config.crimsonIsle.extendNetherFog": "Extend Nether Fog",