aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java5
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java144
-rw-r--r--src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java85
-rw-r--r--src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixer.java90
-rw-r--r--src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix1.java227
-rw-r--r--src/main/java/de/hysky/skyblocker/config/datafixer/ConfigSchema.java29
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionViewScreen.java21
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java11
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/datafixer/JsonHelper.java107
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json4
-rw-r--r--src/test/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixerTest.java32
-rw-r--r--src/test/resources/assets/skyblocker/config/skyblocker-v1.json555
-rw-r--r--src/test/resources/assets/skyblocker/config/skyblocker-v2.json573
15 files changed, 1725 insertions, 162 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index f3bb8d9d..3c6f33a2 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -3,6 +3,7 @@ package de.hysky.skyblocker;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import de.hysky.skyblocker.config.datafixer.ConfigDataFixer;
import de.hysky.skyblocker.config.ImageRepoLoader;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.debug.Debug;
@@ -98,6 +99,7 @@ public class SkyblockerMod implements ClientModInitializer {
@Override
public void onInitializeClient() {
ClientTickEvents.END_CLIENT_TICK.register(this::tick);
+ ConfigDataFixer.apply();
Utils.init();
SkyblockerConfigManager.init();
SkyblockerScreen.initClass();
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index c591ba14..b7a711ab 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -5,7 +5,7 @@ import dev.isxander.yacl3.config.v2.api.SerialEntry;
public class SkyblockerConfig {
@SerialEntry
- public int version = 2;
+ public int version = SkyblockerConfigManager.CONFIG_VERSION;
@SerialEntry
public GeneralConfig general = new GeneralConfig();
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java
index 4b8e56df..688b85aa 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java
@@ -26,10 +26,11 @@ import java.lang.StackWalker.Option;
import java.nio.file.Path;
public class SkyblockerConfigManager {
- private static final Path PATH = FabricLoader.getInstance().getConfigDir().resolve("skyblocker-2.json");
+ public static final int CONFIG_VERSION = 2;
+ private static final Path CONFIG_FILE = FabricLoader.getInstance().getConfigDir().resolve("skyblocker.json");
private static final ConfigClassHandler<SkyblockerConfig> HANDLER = ConfigClassHandler.createBuilder(SkyblockerConfig.class)
.serializer(config -> GsonConfigSerializerBuilder.create(config)
- .setPath(PATH)
+ .setPath(CONFIG_FILE)
.setJson5(false)
.appendGsonBuilder(builder -> builder
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java
index 1200261d..64c67417 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java
@@ -48,14 +48,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button1.item.count,
() -> config.quickNav.button1.item.count,
newValue -> config.quickNav.button1.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button1.item.nbt,
- () -> config.quickNav.button1.item.nbt,
- newValue -> config.quickNav.button1.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button1.item.components,
+ () -> config.quickNav.button1.item.components,
+ newValue -> config.quickNav.button1.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -97,14 +97,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button2.item.count,
() -> config.quickNav.button2.item.count,
newValue -> config.quickNav.button2.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button2.item.nbt,
- () -> config.quickNav.button2.item.nbt,
- newValue -> config.quickNav.button2.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button2.item.components,
+ () -> config.quickNav.button2.item.components,
+ newValue -> config.quickNav.button2.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -146,14 +146,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button3.item.count,
() -> config.quickNav.button3.item.count,
newValue -> config.quickNav.button3.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button3.item.nbt,
- () -> config.quickNav.button3.item.nbt,
- newValue -> config.quickNav.button3.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button3.item.components,
+ () -> config.quickNav.button3.item.components,
+ newValue -> config.quickNav.button3.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -195,14 +195,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button4.item.count,
() -> config.quickNav.button4.item.count,
newValue -> config.quickNav.button4.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button4.item.nbt,
- () -> config.quickNav.button4.item.nbt,
- newValue -> config.quickNav.button4.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button4.item.components,
+ () -> config.quickNav.button4.item.components,
+ newValue -> config.quickNav.button4.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -244,14 +244,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button5.item.count,
() -> config.quickNav.button5.item.count,
newValue -> config.quickNav.button5.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button5.item.nbt,
- () -> config.quickNav.button5.item.nbt,
- newValue -> config.quickNav.button5.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button5.item.components,
+ () -> config.quickNav.button5.item.components,
+ newValue -> config.quickNav.button5.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -293,14 +293,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button6.item.count,
() -> config.quickNav.button6.item.count,
newValue -> config.quickNav.button6.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button6.item.nbt,
- () -> config.quickNav.button6.item.nbt,
- newValue -> config.quickNav.button6.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button6.item.components,
+ () -> config.quickNav.button6.item.components,
+ newValue -> config.quickNav.button6.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -342,14 +342,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button7.item.count,
() -> config.quickNav.button7.item.count,
newValue -> config.quickNav.button7.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button7.item.nbt,
- () -> config.quickNav.button7.item.nbt,
- newValue -> config.quickNav.button7.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button7.item.components,
+ () -> config.quickNav.button7.item.components,
+ newValue -> config.quickNav.button7.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -391,14 +391,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button8.item.count,
() -> config.quickNav.button8.item.count,
newValue -> config.quickNav.button8.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button8.item.nbt,
- () -> config.quickNav.button8.item.nbt,
- newValue -> config.quickNav.button8.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button8.item.components,
+ () -> config.quickNav.button8.item.components,
+ newValue -> config.quickNav.button8.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -440,14 +440,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button9.item.count,
() -> config.quickNav.button9.item.count,
newValue -> config.quickNav.button9.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button9.item.nbt,
- () -> config.quickNav.button9.item.nbt,
- newValue -> config.quickNav.button9.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button9.item.components,
+ () -> config.quickNav.button9.item.components,
+ newValue -> config.quickNav.button9.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -489,14 +489,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button10.item.count,
() -> config.quickNav.button10.item.count,
newValue -> config.quickNav.button10.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button10.item.nbt,
- () -> config.quickNav.button10.item.nbt,
- newValue -> config.quickNav.button10.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button10.item.components,
+ () -> config.quickNav.button10.item.components,
+ newValue -> config.quickNav.button10.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -538,14 +538,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button11.item.count,
() -> config.quickNav.button11.item.count,
newValue -> config.quickNav.button11.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button11.item.nbt,
- () -> config.quickNav.button11.item.nbt,
- newValue -> config.quickNav.button11.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button11.item.components,
+ () -> config.quickNav.button11.item.components,
+ newValue -> config.quickNav.button11.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
@@ -587,14 +587,14 @@ public class QuickNavigationCategory {
.binding(defaults.quickNav.button12.item.count,
() -> config.quickNav.button12.item.count,
newValue -> config.quickNav.button12.item.count = newValue)
- .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64))
+ .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99))
.build())
.option(Option.<String>createBuilder()
- .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip")))
- .binding(defaults.quickNav.button12.item.nbt,
- () -> config.quickNav.button12.item.nbt,
- newValue -> config.quickNav.button12.item.nbt = newValue)
+ .name(Text.translatable("skyblocker.config.quickNav.button.item.components"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip")))
+ .binding(defaults.quickNav.button12.item.components,
+ () -> config.quickNav.button12.item.components,
+ newValue -> config.quickNav.button12.item.components = newValue)
.controller(StringControllerBuilder::create)
.build())
.option(Option.<String>createBuilder()
diff --git a/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java
index 2c4347b6..ba863e33 100644
--- a/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java
@@ -25,12 +25,12 @@ public class QuickNavigationConfig {
*/
@SerialEntry
public QuickNavItem button4 = new QuickNavItem(true,
- new ItemData("leather_chestplate", 1, "tag:{display:{color:8991416}}"), "Wardrobe \\([12]/2\\)",
+ new ItemData("leather_chestplate", 1, "[minecraft:dyed_color={rgb:8991416}]"), "Wardrobe \\([12]/2\\)",
"/wardrobe");
@SerialEntry
public QuickNavItem button5 = new QuickNavItem(true, new ItemData("player_head", 1,
- "tag:{SkullOwner:{Id:[I;-2081424676,-57521078,-2073572414,158072763],Properties:{textures:[{Value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0=\"}]}}}"),
+ "[minecraft:profile={id:[I;-2081424676,-57521078,-2073572414,158072763],name:\"\",properties:[{name:\"textures\",value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0=\"}]}]"),
"Sack of Sacks", "/sacks");
/* REGEX Explanation
@@ -44,17 +44,17 @@ public class QuickNavigationConfig {
@SerialEntry
public QuickNavItem button7 = new QuickNavItem(true, new ItemData("player_head", 1,
- "tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}"),
+ "[minecraft:profile={id:[I;-300151517,-631415889,-1193921967,-1821784279],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}]"),
"none", "/hub");
@SerialEntry
public QuickNavItem button8 = new QuickNavItem(true, new ItemData("player_head", 1,
- "tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}"),
+ "[minecraft:profile={id:[I;1605800870,415127827,-1236127084,15358548],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}]"),
"none", "/warp dungeon_hub");
@SerialEntry
public QuickNavItem button9 = new QuickNavItem(true, new ItemData("player_head", 1,
- "tag:{SkullOwner:{Id:[I;-562285948,532499670,-1705302742,775653035],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0=\"}]}}}"),
+ "[minecraft:profile={id:[I;-562285948,532499670,-1705302742,775653035],name:\"\",properties:[{name:\"textures\",value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0=\"}]}]"),
"Visit prtl", "/visit prtl");
@SerialEntry
@@ -68,73 +68,6 @@ public class QuickNavigationConfig {
@SerialEntry
public QuickNavItem button12 = new QuickNavItem(true, new ItemData("crafting_table"), "Craft Item", "/craft");
- public static class QuickNav {
- @SerialEntry
- public boolean enableQuickNav = true;
-
- @SerialEntry
- public QuickNavItem button1 = new QuickNavItem(true, new ItemData("diamond_sword"), "Your Skills", "/skills");
-
- @SerialEntry
- public QuickNavItem button2 = new QuickNavItem(true, new ItemData("painting"), "Collections", "/collection");
-
- /* REGEX Explanation
- * "Pets" : simple match on letters
- * "(?: \\(\\d+\\/\\d+\\))?" : optional match on the non-capturing group for the page in the format " ($number/$number)"
- */
- @SerialEntry
- public QuickNavItem button3 = new QuickNavItem(true, new ItemData("bone"), "Pets(:? \\(\\d+\\/\\d+\\))?", "/pets");
-
- /* REGEX Explanation
- * "Wardrobe" : simple match on letters
- * " \\([12]\\/2\\)" : match on the page either " (1/2)" or " (2/2)"
- */
- @SerialEntry
- public QuickNavItem button4 = new QuickNavItem(true,
- new ItemData("leather_chestplate", 1, "tag:{display:{color:8991416}}"), "Wardrobe \\([12]/2\\)",
- "/wardrobe");
-
- @SerialEntry
- public QuickNavItem button5 = new QuickNavItem(true, new ItemData("player_head", 1,
- "tag:{SkullOwner:{Id:[I;-2081424676,-57521078,-2073572414,158072763],Properties:{textures:[{Value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0=\"}]}}}"),
- "Sack of Sacks", "/sacks");
-
- /* REGEX Explanation
- * "(?:Rift )?" : optional match on the non-capturing group "Rift "
- * "Storage" : simple match on letters
- * "(?: \\([12]\\/2\\))?" : optional match on the non-capturing group " (1/2)" or " (2/2)"
- */
- @SerialEntry
- public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"),
- "(?:Rift )?Storage(?: \\(1/2\\))?", "/storage");
-
- @SerialEntry
- public QuickNavItem button7 = new QuickNavItem(true, new ItemData("player_head", 1,
- "tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}"),
- "none", "/hub");
-
- @SerialEntry
- public QuickNavItem button8 = new QuickNavItem(true, new ItemData("player_head", 1,
- "tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}"),
- "none", "/warp dungeon_hub");
-
- @SerialEntry
- public QuickNavItem button9 = new QuickNavItem(true, new ItemData("player_head", 1,
- "tag:{SkullOwner:{Id:[I;-562285948,532499670,-1705302742,775653035],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0=\"}]}}}"),
- "Visit prtl", "/visit prtl");
-
- @SerialEntry
- public QuickNavItem button10 = new QuickNavItem(true, new ItemData("enchanting_table"), "Enchant Item",
- "/etable");
-
-
- @SerialEntry
- public QuickNavItem button11 = new QuickNavItem(true, new ItemData("anvil"), "Anvil", "/anvil");
-
- @SerialEntry
- public QuickNavItem button12 = new QuickNavItem(true, new ItemData("crafting_table"), "Craft Item", "/craft");
- }
-
public static class QuickNavItem {
public QuickNavItem(Boolean render, ItemData itemData, String uiTitle, String clickEvent) {
this.render = render;
@@ -157,16 +90,16 @@ public class QuickNavigationConfig {
}
public static class ItemData {
- public ItemData(String id, int count, String nbt) {
+ public ItemData(String id, int count, String components) {
this.id = id;
this.count = count;
- this.nbt = nbt;
+ this.components = components;
}
public ItemData(String id) {
this.id = id;
this.count = 1;
- this.nbt = "";
+ this.components = "[]";
}
@SerialEntry
@@ -176,6 +109,6 @@ public class QuickNavigationConfig {
public int count;
@SerialEntry
- public String nbt;
+ public String components;
}
}
diff --git a/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixer.java b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixer.java
new file mode 100644
index 00000000..97261c76
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixer.java
@@ -0,0 +1,90 @@
+package de.hysky.skyblocker.config.datafixer;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.mojang.datafixers.DSL;
+import com.mojang.datafixers.DataFixer;
+import com.mojang.datafixers.DataFixerBuilder;
+import com.mojang.datafixers.schemas.Schema;
+import com.mojang.logging.LogUtils;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.JsonOps;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.datafixer.JsonHelper;
+import net.fabricmc.loader.api.FabricLoader;
+import org.slf4j.Logger;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class ConfigDataFixer {
+ protected static final Logger LOGGER = LogUtils.getLogger();
+ private static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir();
+ public static final DSL.TypeReference CONFIG_TYPE = () -> "config";
+
+ public static void apply() {
+ apply(CONFIG_DIR.resolve("skyblocker.json"), CONFIG_DIR.resolve("skyblocker.json.old"));
+ }
+
+ public static void apply(Path configDir, Path backupDir) {
+ //User is new - has no config file (or maybe config folder)
+ if (!Files.exists(CONFIG_DIR) || !Files.exists(configDir)) return;
+
+ //Should never be null if the file exists unless its malformed JSON or something in which case well it gets reset
+ JsonObject oldConfig = loadConfig(configDir);
+ if (oldConfig == null || JsonHelper.getInt(oldConfig, "version").orElse(1) == SkyblockerConfigManager.CONFIG_VERSION) return;
+
+ JsonObject newConfig = apply(oldConfig);
+
+ //Write the updated file
+ if (!writeConfig(configDir, newConfig)) {
+ LOGGER.error(LogUtils.FATAL_MARKER, "[Skyblocker Config Data Fixer] Failed to fix up config file!");
+ writeConfig(backupDir, oldConfig);
+ }
+ }
+
+ public static JsonObject apply(JsonObject oldConfig) {
+ long start = System.currentTimeMillis();
+
+ JsonObject newConfig = build().update(CONFIG_TYPE, new Dynamic<>(JsonOps.INSTANCE, oldConfig), JsonHelper.getInt(oldConfig, "version").orElse(1), SkyblockerConfigManager.CONFIG_VERSION).getValue().getAsJsonObject();
+
+ long end = System.currentTimeMillis();
+ LOGGER.info("[Skyblocker Config Data Fixer] Applied datafixers in {} ms!", end - start);
+ return newConfig;
+ }
+
+ private static DataFixer build() {
+ DataFixerBuilder builder = new DataFixerBuilder(SkyblockerConfigManager.CONFIG_VERSION);
+
+ builder.addSchema(1, ConfigSchema::new);
+ Schema schema2 = builder.addSchema(2, Schema::new);
+ builder.addFixer(new ConfigFix1(schema2, true));
+
+ return builder.buildUnoptimized();
+ }
+
+ private static JsonObject loadConfig(Path path) {
+ try (BufferedReader reader = Files.newBufferedReader(path)) {
+ return JsonParser.parseReader(reader).getAsJsonObject();
+ } catch (Throwable t) {
+ LOGGER.error("[Skyblocker Config Data Fixer] Failed to load config file!", t);
+ }
+
+ return null;
+ }
+
+ private static boolean writeConfig(Path path, JsonObject config) {
+ try (BufferedWriter writer = Files.newBufferedWriter(path)) {
+ SkyblockerMod.GSON.toJson(config, writer);
+
+ return true;
+ } catch (Throwable t) {
+ LOGGER.error("[Skyblocker Config Data Fixer] Failed to save config file at {}!", path, t);
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix1.java b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix1.java
new file mode 100644
index 00000000..405a2781
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix1.java
@@ -0,0 +1,227 @@
+package de.hysky.skyblocker.config.datafixer;
+
+import com.mojang.datafixers.DSL;
+import com.mojang.datafixers.DataFix;
+import com.mojang.datafixers.DataFixUtils;
+import com.mojang.datafixers.TypeRewriteRule;
+import com.mojang.datafixers.schemas.Schema;
+import com.mojang.logging.LogUtils;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.OptionalDynamic;
+import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.StringNbtReader;
+
+import java.util.Locale;
+
+public class ConfigFix1 extends DataFix {
+ public ConfigFix1(Schema outputSchema, boolean changesType) {
+ super(outputSchema, changesType);
+ }
+
+ @Override
+ protected TypeRewriteRule makeRule() {
+ return fixTypeEverywhereTyped(
+ "ConfigFix1",
+ getInputSchema().getType(ConfigDataFixer.CONFIG_TYPE),
+ typed -> typed.update(DSL.remainderFinder(), this::fix)
+ );
+ }
+
+ private <T> Dynamic<T> fix(Dynamic<T> dynamic) {
+ return fixMisc(fixQuickNav(fixChat(fixSlayers(fixOtherLocations(fixFarming(fixMining(fixCrimsonIsle(fixDungeons(fixHelpers(fixUIAndVisuals(fixGeneral(fixVersion(dynamic)))))))))))));
+ }
+
+ private <T> Dynamic<T> fixVersion(Dynamic<T> dynamic) {
+ return dynamic.set("version", dynamic.createInt(DataFixUtils.getVersion(getVersionKey())));
+ }
+
+ private static <T> Dynamic<T> fixGeneral(Dynamic<T> dynamic) {
+ return dynamic.update("general", general -> general.update("itemTooltip", itemTooltip -> itemTooltip.setFieldIfPresent("dungeonQuality", general.get("dungeonQuality").result())).remove("dungeonQuality"));
+ }
+
+ private static <T> Dynamic<T> fixUIAndVisuals(Dynamic<T> dynamic) {
+ OptionalDynamic<T> general = dynamic.get("general");
+ return dynamic.set("uiAndVisuals", dynamic.emptyMap()
+ .setFieldIfPresent("compactorDeletorPreview", general.get("compactorDeletorPreview").result())
+ .setFieldIfPresent("dontStripSkinAlphaValues", general.get("dontStripSkinAlphaValues").result())
+ .setFieldIfPresent("backpackPreviewWithoutShift", general.get("backpackPreviewWithoutShift").result())
+ .setFieldIfPresent("hideEmptyTooltips", general.get("hideEmptyTooltips").result())
+ .setFieldIfPresent("fancyCraftingTable", general.get("fancyCraftingTable").result())
+ .setFieldIfPresent("hideStatusEffectOverlay", general.get("hideStatusEffectOverlay").result())
+ .setFieldIfPresent("chestValue", general.get("chestValue").result())
+ .setFieldIfPresent("itemCooldown", general.get("itemCooldown").result())
+ .setFieldIfPresent("titleContainer", general.get("titleContainer").result())
+ .setFieldIfPresent("tabHud", general.get("tabHud").result())
+ .setFieldIfPresent("fancyAuctionHouse", general.get("fancyAuctionHouse").result())
+ .setFieldIfPresent("bars", general.get("bars").result())
+ .setFieldIfPresent("waypoints", general.get("waypoints").result())
+ .setFieldIfPresent("teleportOverlay", general.get("teleportOverlay").result())
+ .setFieldIfPresent("searchOverlay", general.get("searchOverlay").result())
+ .setFieldIfPresent("flameOverlay", general.get("flameOverlay").result())
+ ).update("general", newGeneral -> newGeneral
+ .remove("compactorDeletorPreview")
+ .remove("dontStripSkinAlphaValues")
+ .remove("backpackPreviewWithoutShift")
+ .remove("hideEmptyTooltips")
+ .remove("fancyCraftingTable")
+ .remove("hideStatusEffectOverlay")
+ .remove("chestValue")
+ .remove("itemCooldown")
+ .remove("titleContainer")
+ .remove("tabHud")
+ .remove("fancyAuctionHouse")
+ .remove("bars")
+ .remove("waypoints")
+ .remove("teleportOverlay")
+ .remove("searchOverlay")
+ .remove("flameOverlay")
+ );
+ }
+
+ private static <T> Dynamic<T> fixHelpers(Dynamic<T> dynamic) {
+ OptionalDynamic<T> general = dynamic.get("general");
+ return dynamic.set("helpers", dynamic.emptyMap()
+ .setFieldIfPresent("enableNewYearCakesHelper", general.get("enableNewYearCakesHelper").result())
+ .setFieldIfPresent("mythologicalRitual", general.get("mythologicalRitual").result())
+ .setFieldIfPresent("experiments", general.get("experiments").result())
+ .setFieldIfPresent("fishing", general.get("fishing").result())
+ .setFieldIfPresent("fairySouls", general.get("fairySouls").result())
+ ).update("general", newGeneral -> newGeneral
+ .remove("enableNewYearCakesHelper")
+ .remove("mythologicalRitual")
+ .remove("experiments")
+ .remove("fishing")
+ .remove("fairySouls")
+ );
+ }
+
+ private static <T> Dynamic<T> fixDungeons(Dynamic<T> dynamic) {
+ OptionalDynamic<T> general = dynamic.get("general");
+ OptionalDynamic<T> dungeons = dynamic.get("locations").get("dungeons");
+ return dynamic.set("dungeons", dynamic.emptyMap()
+ .setFieldIfPresent("fancyPartyFinder", general.get("betterPartyFinder").result())
+ .setFieldIfPresent("croesusHelper", dungeons.get("croesusHelper").result())
+ .setFieldIfPresent("playerSecretsTracker", dungeons.get("playerSecretsTracker").result())
+ .setFieldIfPresent("starredMobGlow", dungeons.get("starredMobGlow").result())
+ .setFieldIfPresent("starredMobBoundingBoxes", dungeons.get("starredMobBoundingBoxes").result())
+ .setFieldIfPresent("allowDroppingProtectedItems", dungeons.get("allowDroppingProtectedItems").result())
+ .set("dungeonMap", dynamic.emptyMap()
+ .setFieldIfPresent("enableMap", dungeons.get("enableMap").result())
+ .setFieldIfPresent("mapScaling", dungeons.get("mapScaling").result())
+ .setFieldIfPresent("mapX", dungeons.get("mapX").result())
+ .setFieldIfPresent("mapY", dungeons.get("mapY").result())
+ )
+ .set("puzzleSolvers", dynamic.emptyMap()
+ .setFieldIfPresent("solveThreeWeirdos", dungeons.get("solveThreeWeirdos").result())
+ .setFieldIfPresent("blazeSolver", dungeons.get("blazeSolver").result())
+ .setFieldIfPresent("creeperSolver", dungeons.get("creeperSolver").result())
+ .setFieldIfPresent("solveTrivia", dungeons.get("solveTrivia").result())
+ .setFieldIfPresent("solveTicTacToe", dungeons.get("solveTicTacToe").result())
+ .setFieldIfPresent("solveWaterboard", dungeons.get("solveWaterboard").result())
+ .setFieldIfPresent("solveBoulder", dungeons.get("solveBoulder").result())
+ .setFieldIfPresent("solveIceFill", dungeons.get("solveIceFill").result())
+ .setFieldIfPresent("solveSilverfish", dungeons.get("solveSilverfish").result())
+ )
+ .set("theProfessor", dynamic.emptyMap()
+ .setFieldIfPresent("fireFreezeStaffTimer", dungeons.get("fireFreezeStaffTimer").result())
+ .setFieldIfPresent("floor3GuardianHealthDisplay", dungeons.get("floor3GuardianHealthDisplay").result())
+ )
+ .setFieldIfPresent("livid", dungeons.get("lividColor").result())
+ .setFieldIfPresent("terminals", dungeons.get("terminals").result())
+ .setFieldIfPresent("secretWaypoints", dungeons.get("secretWaypoints").result())
+ .setFieldIfPresent("mimicMessage", dungeons.get("mimicMessage").result())
+ .setFieldIfPresent("doorHighlight", dungeons.get("doorHighlight").result())
+ .setFieldIfPresent("dungeonScore", dungeons.get("dungeonScore").result())
+ .setFieldIfPresent("dungeonChestProfit", dungeons.get("dungeonChestProfit").result())
+ ).update("locations", locations -> locations.remove("dungeons")).update("general", newGeneral -> newGeneral.remove("betterPartyFinder"));
+ }
+
+ private static <T> Dynamic<T> fixCrimsonIsle(Dynamic<T> dynamic) {
+ return dynamic.setFieldIfPresent("crimsonIsle", dynamic.get("locations").get("crimsonIsle").result()).update("locations", locations -> locations.remove("crimsonIsle"));
+ }
+
+ private static <T> Dynamic<T> fixMining(Dynamic<T> dynamic) {
+ OptionalDynamic<T> dwarvenMines = dynamic.get("locations").get("dwarvenMines");
+ return dynamic.set("mining", dynamic.emptyMap()
+ .setFieldIfPresent("enableDrillFuel", dwarvenMines.get("enableDrillFuel").result())
+ .set("dwarvenMines", dynamic.emptyMap()
+ .setFieldIfPresent("solveFetchur", dwarvenMines.get("solveFetchur").result())
+ .setFieldIfPresent("solvePuzzler", dwarvenMines.get("solvePuzzler").result())
+ )
+ .set("dwarvenHud", dwarvenMines.get("dwarvenHud").result().orElseThrow()
+ .renameField("x", "commissionsX")
+ .renameField("y", "commissionsY")
+ )
+ .setFieldIfPresent("crystalsHud", dwarvenMines.get("crystalsHud").result())
+ .setFieldIfPresent("crystalsWaypoints", dwarvenMines.get("crystalsWaypoints").result())
+ .set("crystalHollows", dynamic.emptyMap()
+ .setFieldIfPresent("metalDetectorHelper", dwarvenMines.get("metalDetectorHelper").result())
+ )
+ ).update("locations", locations -> locations.remove("dwarvenMines"));
+ }
+
+ private static <T> Dynamic<T> fixFarming(Dynamic<T> dynamic) {
+ return dynamic.set("farming", dynamic.emptyMap()
+ .setFieldIfPresent("garden", dynamic.get("locations").get("garden").result())
+ ).update("locations", locations -> locations.remove("garden"));
+ }
+
+ private static <T> Dynamic<T> fixOtherLocations(Dynamic<T> dynamic) {
+ return dynamic.renameField("locations", "otherLocations");
+ }
+
+ private static <T> Dynamic<T> fixSlayers(Dynamic<T> dynamic) {
+ return dynamic.renameField("slayer", "slayers");
+ }
+
+ private static <T> Dynamic<T> fixChat(Dynamic<T> dynamic) {
+ return dynamic.renameField("messages", "chat");
+ }
+
+ private static <T> Dynamic<T> fixQuickNav(Dynamic<T> dynamic) {
+ return dynamic.update("quickNav", quickNav -> quickNav
+ .update("button1", ConfigFix1::fixQuickNavButton)
+ .update("button2", ConfigFix1::fixQuickNavButton)
+ .update("button3", ConfigFix1::fixQuickNavButton)
+ .update("button4", ConfigFix1::fixQuickNavButton)
+ .update("button5", ConfigFix1::fixQuickNavButton)
+ .update("button6", ConfigFix1::fixQuickNavButton)
+ .update("button7", ConfigFix1::fixQuickNavButton)
+ .update("button8", ConfigFix1::fixQuickNavButton)
+ .update("button9", ConfigFix1::fixQuickNavButton)
+ .update("button10", ConfigFix1::fixQuickNavButton)
+ .update("button11", ConfigFix1::fixQuickNavButton)
+ .update("button12", ConfigFix1::fixQuickNavButton));
+ }
+
+ private static <T> Dynamic<T> fixQuickNavButton(Dynamic<T> button) {
+ return button.update("item", item -> item
+ .renameField("itemName", "id")
+ .renameAndFixField("nbt", "components", nbt -> fixNbt(item.get("itemName"), nbt))
+ );
+ }
+
+ private static Dynamic<?> fixNbt(OptionalDynamic<?> id, Dynamic<?> nbt) {
+ try {
+ String itemNbt = "{id:\"minecraft:" + id.asString().getOrThrow().toLowerCase(Locale.ROOT) + "\",Count:1";
+ String extraNbt = nbt.asString().getOrThrow();
+ if (extraNbt.length() > 2) itemNbt += "," + extraNbt;
+ itemNbt += "}";
+
+ ItemStack fixed = ItemStackComponentizationFixer.fixUpItem(StringNbtReader.parse(itemNbt));
+
+ return nbt.createString(ItemStackComponentizationFixer.componentsAsString(fixed));
+ } catch (Exception e) {
+ ConfigDataFixer.LOGGER.error(LogUtils.FATAL_MARKER, "[Skyblocker Config Data Fixer] Failed to convert nbt to components!", e);
+ }
+
+ return nbt.createString("[]");
+ }
+
+ private static <T> Dynamic<T> fixMisc(Dynamic<T> dynamic) {
+ return dynamic.set("misc", dynamic.emptyMap()
+ .setFieldIfPresent("richPresence", dynamic.get("richPresence").result())
+ ).remove("richPresence");
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigSchema.java b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigSchema.java
new file mode 100644
index 00000000..4c821169
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigSchema.java
@@ -0,0 +1,29 @@
+package de.hysky.skyblocker.config.datafixer;
+
+import com.mojang.datafixers.DSL;
+import com.mojang.datafixers.schemas.Schema;
+import com.mojang.datafixers.types.templates.TypeTemplate;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+public class ConfigSchema extends Schema {
+ public ConfigSchema(int versionKey, Schema parent) {
+ super(versionKey, parent);
+ }
+
+ @Override
+ public void registerTypes(Schema schema, Map<String, Supplier<TypeTemplate>> entityTypes, Map<String, Supplier<TypeTemplate>> blockEntityTypes) {
+ schema.registerType(true, ConfigDataFixer.CONFIG_TYPE, DSL::remainder);
+ }
+
+ @Override
+ public Map<String, Supplier<TypeTemplate>> registerEntities(Schema schema) {
+ return Map.of();
+ }
+
+ @Override
+ public Map<String, Supplier<TypeTemplate>> registerBlockEntities(Schema schema) {
+ return Map.of();
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionViewScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionViewScreen.java
index af931eb1..f3db2a25 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionViewScreen.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionViewScreen.java
@@ -5,6 +5,7 @@ import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.render.gui.AbstractCustomHypixelGUI;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.PopupScreen;
+import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.DirectionalLayoutWidget;
import net.minecraft.client.gui.widget.SimplePositioningWidget;
@@ -21,7 +22,9 @@ import net.minecraft.text.TextColor;
import net.minecraft.util.Colors;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
+import org.lwjgl.glfw.GLFW;
+import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -56,6 +59,15 @@ public class AuctionViewScreen extends AbstractCustomHypixelGUI<AuctionHouseScre
}
@Override
+ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
+ if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
+ clickSlot(BACK_BUTTON_SLOT);
+ return true;
+ }
+ return super.keyPressed(keyCode, scanCode, modifiers);
+ }
+
+ @Override
protected void init() {
super.init();
verticalLayout.spacing(2).getMainPositioner().alignHorizontalCenter();
@@ -78,10 +90,13 @@ public class AuctionViewScreen extends AbstractCustomHypixelGUI<AuctionHouseScre
verticalLayout.forEachChild(this::addDrawableChild);
updateLayout();
- addDrawableChild(new ButtonWidget.Builder(Text.literal("<"), button -> this.clickSlot(BACK_BUTTON_SLOT))
+ ButtonWidget backButton = new ButtonWidget.Builder(Text.literal("<"), button -> this.clickSlot(BACK_BUTTON_SLOT))
.position(x + backgroundWidth - 16, y + 4)
.size(12, 12)
- .build());
+ .tooltip(Tooltip.of(Text.literal("or press ESC!")))
+ .build();
+ backButton.setTooltipDelay(Duration.ofSeconds(1));
+ addDrawableChild(backButton);
}
@@ -189,7 +204,7 @@ public class AuctionViewScreen extends AbstractCustomHypixelGUI<AuctionHouseScre
@Override
public void onSlotChange(AuctionHouseScreenHandler handler, int slotId, ItemStack stack) {
- if (stack.isOf(Items.BLACK_STAINED_GLASS_PANE) || slotId == 13) return;
+ if (stack.isOf(Items.BLACK_STAINED_GLASS_PANE) || slotId == 13 || slotId >= handler.getRows() * 9) return;
assert client != null;
if (stack.isOf(Items.RED_TERRACOTTA)) { // Red terracotta shows up when you can cancel it
changeState(BuyState.CANCELLABLE_AUCTION);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java
index 5529e466..a6adf66b 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java
@@ -11,15 +11,15 @@ import net.fabricmc.fabric.api.client.screen.v1.Screens;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.nbt.StringNbtReader;
+import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
import java.util.regex.PatternSyntaxException;
public class QuickNav {
@@ -59,10 +59,9 @@ public class QuickNav {
private static QuickNavButton parseButton(QuickNavigationConfig.QuickNavItem buttonInfo, String screenTitle, int id) throws CommandSyntaxException {
QuickNavigationConfig.ItemData itemData = buttonInfo.item;
- String nbtString = "{id:\"minecraft:" + itemData.id.toLowerCase(Locale.ROOT) + "\",Count:1";
- if (itemData.nbt.length() > 2) nbtString += "," + itemData.nbt;
- nbtString += "}";
+ ItemStack stack = ItemStackComponentizationFixer.fromComponentsString(itemData.id, Math.clamp(itemData.count, 1, 99), itemData.components);
boolean uiTitleMatches = false;
+
try {
uiTitleMatches = screenTitle.matches(buttonInfo.uiTitle);
} catch (PatternSyntaxException e) {
@@ -75,6 +74,6 @@ public class QuickNav {
return new QuickNavButton(id,
uiTitleMatches,
buttonInfo.clickEvent,
- ItemStackComponentizationFixer.fixUpItem(StringNbtReader.parse(nbtString)));
+ stack);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/utils/datafixer/JsonHelper.java b/src/main/java/de/hysky/skyblocker/utils/datafixer/JsonHelper.java
new file mode 100644
index 00000000..f7646d31
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/datafixer/JsonHelper.java
@@ -0,0 +1,107 @@
+package de.hysky.skyblocker.utils.datafixer;
+
+import java.util.Optional;
+import java.util.OptionalInt;
+
+import com.google.gson.JsonObject;
+
+import net.minecraft.util.annotation.MethodsReturnNonnullByDefault;
+
+/**
+ * Helper methods to assist in retrieving values nested in JSON objects.
+ *
+ * All methods are fully null safe, whether it be from passing a {@code null} root object or from encountering a nonexistent or null object/value.
+ *
+ * @author AzureAaron
+ * @see <a href="https://github.com/AzureAaron/aaron-mod/blob/1.20/src/main/java/net/azureaaron/mod/utils/JsonHelper.java">Aaron's Mod's JSON Helper</a>
+ */
+@MethodsReturnNonnullByDefault
+public class JsonHelper {
+
+ public static OptionalInt getInt(JsonObject root, String path) {
+ //If root is null
+ if (root == null) return OptionalInt.empty();
+
+ //Fast path for if we just want the field itself
+ if (!path.contains(".")) {
+ return root.has(path) && !root.get(path).isJsonNull() ? OptionalInt.of(root.get(path).getAsInt()) : OptionalInt.empty();
+ }
+
+ String[] split = path.split("\\.");
+ String propertyName = split[split.length - 1];
+ String[] objects2Traverse = new String[split.length - 1];
+
+ //Get the traversal path
+ System.arraycopy(split, 0, objects2Traverse, 0, split.length - 1);
+
+ JsonObject currentLevel = root;
+
+ for (String objectName : objects2Traverse) {
+ if (currentLevel.has(objectName) && !currentLevel.get(objectName).isJsonNull()) {
+ currentLevel = currentLevel.getAsJsonObject(objectName);
+ } else {
+ return OptionalInt.empty();
+ }
+ }
+
+ return currentLevel.has(propertyName) && !currentLevel.get(propertyName).isJsonNull() ? OptionalInt.of(currentLevel.get(propertyName).getAsInt()) : OptionalInt.empty();
+ }
+
+ public static Optional<Boolean> getBoolean(JsonObject root, String path) {
+ //If root is null
+ if (root == null) return Optional.empty();
+
+ //Fast path for if we just want the field itself
+ if (!path.contains(".")) {
+ return root.has(path) && !root.get(path).isJsonNull() ? Optional.of(root.get(path).getAsBoolean()) : Optional.empty();
+ }
+
+ String[] split = path.split("\\.");
+ String propertyName = split[split.length - 1];
+ String[] objects2Traverse = new String[split.length - 1];
+
+ //Get the traversal path
+ System.arraycopy(split, 0, objects2Traverse, 0, split.length - 1);
+
+ JsonObject currentLevel = root;
+
+ for (String objectName : objects2Traverse) {
+ if (currentLevel.has(objectName) && !currentLevel.get(objectName).isJsonNull()) {
+ currentLevel = currentLevel.getAsJsonObject(objectName);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ return currentLevel.has(propertyName) && !currentLevel.get(propertyName).isJsonNull() ? Optional.of(currentLevel.get(propertyName).getAsBoolean()) : Optional.empty();
+ }
+
+ public static Optional<String> getString(JsonObject root, String path) {
+ //If root is null
+ if (root == null) return Optional.empty();
+
+ //Fast path for if we just want the field itself
+ if (!path.contains(".")) {
+ return root.has(path) && !root.get(path).isJsonNull() ? Optional.of(root.get(path).getAsString()) : Optional.empty();
+ }
+
+ String[] split = path.split("\\.");
+ String propertyName = split[split.length - 1];
+ String[] objects2Traverse = new String[split.length - 1];
+
+ //Get the traversal path
+ System.arraycopy(split, 0, objects2Traverse, 0, split.length - 1);
+
+ JsonObject currentLevel = root;
+
+ for (String objectName : objects2Traverse) {
+ if (currentLevel.has(objectName) && !currentLevel.get(objectName).isJsonNull()) {
+ currentLevel = currentLevel.getAsJsonObject(objectName);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ return currentLevel.has(propertyName) && !currentLevel.get(propertyName).isJsonNull() ? Optional.of(currentLevel.get(propertyName).getAsString()) : Optional.empty();
+ }
+}
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index c5ebd9d4..6d2433c0 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -471,10 +471,10 @@
"skyblocker.config.quickNav": "Quick Navigation",
"skyblocker.config.quickNav.button": "Button %d",
"skyblocker.config.quickNav.button.clickEvent": "Click event",
+ "skyblocker.config.quickNav.button.item.components": "Item Components",
+ "skyblocker.config.quickNav.button.item.components.@Tooltip": "A string of item components enclosed in square brackets, which is the same format as the /give command.\n\nExample: [minecraft:enchantment_glint_override=true]",
"skyblocker.config.quickNav.button.item.count": "Item Count",
"skyblocker.config.quickNav.button.item.itemName": "Item ID",
- "skyblocker.config.quickNav.button.item.nbt": "NBT",
- "skyblocker.config.quickNav.button.item.nbt.@Tooltip": "\u00a7c\u00a7lWARNING: THIS MUST BE CONFIGURED WITH ITEM NBT NOT ITEM COMPONENTS! \n\n\u00a7fIn the future we will automatically migrate your config to use item components!",
"skyblocker.config.quickNav.button.render": "Render",
"skyblocker.config.quickNav.button.uiTitle": "UI Title",
"skyblocker.config.quickNav.enableQuickNav": "Enable Quick Navigation",
diff --git a/src/test/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixerTest.java b/src/test/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixerTest.java
new file mode 100644
index 00000000..aab10084
--- /dev/null
+++ b/src/test/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixerTest.java
@@ -0,0 +1,32 @@
+package de.hysky.skyblocker.config.datafixer;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
+import net.minecraft.Bootstrap;
+import net.minecraft.SharedConstants;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStreamReader;
+
+public class ConfigDataFixerTest {
+ private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
+
+ @BeforeAll
+ public static void setupEnvironment() {
+ SharedConstants.createGameVersion();
+ Bootstrap.initialize();
+ }
+
+ @Test
+ void testDataFixer1() {
+ @SuppressWarnings("DataFlowIssue")
+ JsonObject oldConfig = GSON.fromJson(new InputStreamReader(ConfigDataFixerTest.class.getResourceAsStream("/assets/skyblocker/config/skyblocker-v1.json")), JsonObject.class);
+ @SuppressWarnings("DataFlowIssue")
+ JsonObject expectedNewConfig = GSON.fromJson(new InputStreamReader(ConfigDataFixerTest.class.getResourceAsStream("/assets/skyblocker/config/skyblocker-v2.json")), JsonObject.class);
+
+ Assertions.assertEquals(expectedNewConfig, ConfigDataFixer.apply(oldConfig));
+ }
+}
diff --git a/src/test/resources/assets/skyblocker/config/skyblocker-v1.json b/src/test/resources/assets/skyblocker/config/skyblocker-v1.json
new file mode 100644
index 00000000..20620a57
--- /dev/null
+++ b/src/test/resources/assets/skyblocker/config/skyblocker-v1.json
@@ -0,0 +1,555 @@
+{
+ "version": 1,
+ "general": {
+ "enableTips": true,
+ "acceptReparty": true,
+ "betterPartyFinder": true,
+ "fancyCraftingTable": true,
+ "backpackPreviewWithoutShift": true,
+ "compactorDeletorPreview": true,
+ "hideEmptyTooltips": true,
+ "hideStatusEffectOverlay": false,
+ "dontStripSkinAlphaValues": true,
+ "dungeonQuality": true,
+ "enableNewYearCakesHelper": true,
+ "tabHud": {
+ "tabHudEnabled": true,
+ "tabHudScale": 100,
+ "enableHudBackground": true,
+ "plainPlayerNames": false,
+ "nameSorting": "ALPHABETICAL"
+ },
+ "bars": {
+ "enableBars": true,
+ "barPositions": {
+ "healthBarPosition": "LAYER1",
+ "manaBarPosition": "LAYER1",
+ "defenceBarPosition": "RIGHT",
+ "experienceBarPosition": "RIGHT"
+ }
+ },
+ "experiments": {
+ "enableChronomatronSolver": true,
+ "enableSuperpairsSolver": true,
+ "enableUltrasequencerSolver": true
+ },
+ "fishing": {
+ "enableFishingHelper": true,
+ "enableFishingTimer": false,
+ "changeTimerColor": true,
+ "fishingTimerScale": 1.0,
+ "hideOtherPlayersRods": false
+ },
+ "fairySouls": {
+ "enableFairySoulsHelper": true,
+ "highlightFoundSouls": false,
+ "highlightOnlyNearbySouls": false
+ },
+ "mythologicalRitual": {
+ "enableMythologicalRitualHelper": true
+ },
+ "itemCooldown": {
+ "enableItemCooldowns": true
+ },
+ "shortcuts": {
+ "enableShortcuts": true,
+ "enableCommandShortcuts": true,
+ "enableCommandArgShortcuts": true
+ },
+ "waypoints": {
+ "enableWaypoints": true,
+ "waypointType": "WAYPOINT"
+ },
+ "quiverWarning": {
+ "enableQuiverWarning": true,
+ "enableQuiverWarningInDungeons": true,
+ "enableQuiverWarningAfterDungeon": true
+ },
+ "itemList": {
+ "enableItemList": true
+ },
+ "itemTooltip": {
+ "enableNPCPrice": true,
+ "enableMotesPrice": true,
+ "enableAvgBIN": true,
+ "avg": "THREE_DAY",
+ "enableLowestBIN": true,
+ "enableBazaarPrice": true,
+ "enableObtainedDate": true,
+ "enableMuseumInfo": true,
+ "enableExoticTooltip": true,
+ "enableAccessoriesHelper": true
+ },
+ "itemInfoDisplay": {
+ "attributeShardInfo": true,
+ "itemRarityBackgrounds": true,
+ "itemRarityBackgroundStyle": "CIRCULAR",
+ "itemRarityBackgroundsOpacity": 0.5
+ },
+ "itemProtection": {
+ "slotLockStyle": "FANCY"
+ },
+ "wikiLookup": {
+ "enableWikiLookup": true,
+ "officialWiki": false
+ },
+ "chestValue": {
+ "enableChestValue": true,
+ "color": "DARK_GREEN",
+ "incompleteColor": "BLUE"
+ },
+ "specialEffects": {
+ "rareDungeonDropEffects": true
+ },
+ "hitbox": {
+ "oldFarmlandHitbox": true,
+ "oldLeverHitbox": false
+ },
+ "titleContainer": {
+ "titleContainerScale": 100.0,
+ "x": 482,
+ "y": 170,
+ "direction": "VERTICAL",
+ "alignment": "MIDDLE"
+ },
+ "teleportOverlay": {
+ "enableTeleportOverlays": true,
+ "enableWeirdTransmission": true,
+ "enableInstantTransmission": true,
+ "enableEtherTransmission": true,
+ "enableSinrecallTransmission": true,
+ "enableWitherImpact": true
+ },
+ "flameOverlay": {
+ "flameHeight": 0,
+ "flameOpacity": 0
+ },
+ "searchOverlay": {
+ "enableBazaar": true,
+ "enableAuctionHouse": true,
+ "keepPreviousSearches": false,
+ "maxSuggestions": 5,
+ "historyLength": 5,
+ "enableCommands": false,
+ "bazaarHistory": [
+ "Enchanted Snow Block",
+ "Recombobulator 3000",
+ "Enchanted Snow Block",
+ "Recombobulator 3000",
+ "Enchanted Snow Block"
+ ],
+ "auctionHistory": [
+ "God Potion",
+ "New Year Cake (Year 30",
+ "New Year Cake (Year 31",
+ "New Year Cake (Year 32",
+ "New Year Cake (Year 2"
+ ]
+ },
+ "fancyAuctionHouse": {
+ "enabled": true,
+ "highlightCheapBIN": true
+ },
+ "lockedSlots": [
+ 3,
+ 8,
+ 0
+ ],
+ "protectedItems": [
+ "10f84455-c063-4e3e-b6eb-cc485fe74a06",
+ "64e491fa-64ed-4abf-829a-940e1fb295d2",
+ "23a619b6-f487-419e-8598-10b8f21dc80a",
+ "416aa42d-27d4-4284-913f-d03fa1f528c3",
+ "4e2a7f16-7d93-4069-bfbd-a8ff8e42f285",
+ "63ec4f84-2005-45e5-a4b7-0c9ae84dc612",
+ "aba2b017-91e8-4263-ae9e-5a10f8dc91fe",
+ "f1391873-5126-496d-b0fe-c861faeaf681",
+ "bb84cfea-3fdd-4579-b5fa-907cd8123593",
+ "a604f533-49f6-4190-b971-08f725231229",
+ "333cba8d-3136-4494-9fa0-40dff482ef66",
+ "91f49338-fc0b-4601-a704-aabc44853318",
+ "d76e5eca-be47-4d0b-973c-3ec30fd176dc",
+ "301c58a7-9b8a-4c4e-9e8d-3e8a9471a916",
+ "c8ab629e-fc5b-4af0-8053-acb155411c9b",
+ "333a079f-93d4-48f1-af9f-862b1ecc417c",
+ "0843d8b0-b6a6-47fc-afde-8eee8c5455d9",
+ "8bcf2a99-68e6-4646-ac04-4c72e55c6818",
+ "214333db-d71c-4080-8487-00b4666bb9a4",
+ "1eba38b5-e0f6-4862-bf19-e2df33e21ce4",
+ "6b42c77f-1891-43a4-a9ea-4454e299a059",
+ "71ce1812-d1fd-4e71-98ad-8b863759a82f",
+ "253489d9-49d0-46c8-b3f8-b70c3663c5d2",
+ "6033eff2-879d-494f-8dae-696fc8a3c82e",
+ "5f6090c8-cbc4-4030-97ac-5018c151cc15",
+ "64162e02-91fb-4ec3-a57c-866436a99a7a",
+ "a299c718-c66b-4de5-9747-71c172ac4601",
+ "e21172ce-0a41-426a-a445-84a28677cd65",
+ "4bfd23bf-ceb8-4951-874d-e61633e088d8",
+ "575ce2c9-251e-4435-9020-de1a2e24b1d0",
+ "b06b8fe2-470a-43f3-b844-658472f20996",
+ "e54dea3d-ca45-451e-9ae4-0e5fb87e97b2",
+ "67b47348-da46-4257-833a-dd23fb074cc6",
+ "9f7597ec-c3c9-46df-9f36-8e76c8745a9d",
+ "e297dcef-2f3d-4fb3-9781-aaff6017178c",
+ "ad9a598d-e5c9-4293-92c8-5e41bebe913c",
+ "fa51c660-b262-4826-bed8-6138236919cb"
+ ],
+ "customItemNames": {},
+ "customDyeColors": {},
+ "customArmorTrims": {
+ "f1391873-5126-496d-b0fe-c861faeaf681": {
+ "material": "minecraft:amethyst",
+ "pattern": "minecraft:raiser"
+ },
+ "81409da1-610c-445a-aba2-4c95a3cabbf2": {
+ "material": "minecraft:amethyst",
+ "pattern": "minecraft:coast"
+ }
+ },
+ "customAnimatedDyes": {
+ "f1391873-5126-496d-b0fe-c861faeaf681": {
+ "color1": 16711680,
+ "color2": 11154282,
+ "samples": 10,
+ "cycleBack": true,
+ "tickDelay": 4
+ }
+ }
+ },
+ "locations": {
+ "barn": {
+ "solveHungryHiker": true,
+ "solveTreasureHunter": true
+ },
+ "crimsonIsle": {
+ "kuudra": {
+ "supplyWaypoints": true,
+ "fuelWaypoints": true,
+ "suppliesAndFuelWaypointType": "WAYPOINT",
+ "ballistaBuildWaypoints": true,
+ "safeSpotWaypoints": true,
+ "pearlWaypoints": true,
+ "noArrowPoisonWarning": true,
+ "arrowPoisonThreshold": 16
+ }
+ },
+ "dungeons": {
+ "secretWaypoints": {
+ "enableRoomMatching": true,
+ "enableSecretWaypoints": true,
+ "waypointType": "OUTLINED_HIGHLIGHT",
+ "showSecretText": true,
+ "enableEntranceWaypoints": true,
+ "enableSuperboomWaypoints": true,
+ "enableChestWaypoints": true,
+ "enableItemWaypoints": true,
+ "enableBatWaypoints": true,
+ "enableWitherWaypoints": true,
+ "enableLeverWaypoints": true,
+ "enableFairySoulWaypoints": true,
+ "enableStonkWaypoints": false,
+ "enableAotvWaypoints": true,
+ "enablePearlWaypoints": true,
+ "enableDefaultWaypoints": true
+ },
+ "doorHighlight": {
+ "enableDoorHighlight": true,
+ "doorHighlightType": "OUTLINED_HIGHLIGHT"
+ },
+ "dungeonScore": {
+ "enableDungeonScore270Message": false,
+ "enableDungeonScore270Title": false,
+ "enableDungeonScore270Sound": false,
+ "dungeonScore270Message": "270 Score Reached!",
+ "enableDungeonScore300Message": true,
+ "enableDungeonScore300Title": true,
+ "enableDungeonScore300Sound": true,
+ "dungeonScore300Message": "[做得好] 300 Social Credit has been added to your account. Enjoy your boss fight.",
+ "enableDungeonCryptsMessage": true,
+ "dungeonCryptsMessageThreshold": 250,
+ "dungeonCryptsMessage": "We only have [crypts] crypts out of 5, we need more!",
+ "enableScoreHUD": true,
+ "scoreX": 25,
+ "scoreY": 134,
+ "scoreScaling": 1.0
+ },
+ "dungeonChestProfit": {
+ "enableProfitCalculator": true,
+ "includeKismet": true,
+ "includeEssence": false,
+ "croesusProfit": true,
+ "neutralThreshold": 1000,
+ "neutralColor": "DARK_GRAY",
+ "profitColor": "DARK_GREEN",
+ "lossColor": "RED",
+ "incompleteColor": "BLUE"
+ },
+ "mimicMessage": {
+ "sendMimicMessage": true,
+ "mimicMessage": "Mimic dead!"
+ },
+ "croesusHelper": true,
+ "enableMap": true,
+ "mapScaling": 1.0,
+ "mapX": 2,
+ "mapY": 2,
+ "playerSecretsTracker": true,
+ "starredMobGlow": true,
+ "starredMobBoundingBoxes": true,
+ "solveThreeWeirdos": true,
+ "blazeSolver": true,
+ "creeperSolver": true,
+ "solveTrivia": true,
+ "solveTicTacToe": true,
+ "solveWaterboard": true,
+ "solveBoulder": true,
+ "solveIceFill": true,
+ "solveSilverfish": true,
+ "fireFreezeStaffTimer": true,
+ "floor3GuardianHealthDisplay": true,
+ "allowDroppingProtectedItems": false,
+ "lividColor": {
+ "enableLividColorGlow": true,
+ "enableLividColorText": true,
+ "enableLividColorTitle": true,
+ "lividColorText": "[color] is sus"
+ },
+ "terminals": {
+ "solveColor": true,
+ "solveOrder": true,
+ "solveStartsWith": true
+ }
+ },
+ "dwarvenMines": {
+ "enableDrillFuel": true,
+ "solveFetchur": true,
+ "solvePuzzler": true,
+ "metalDetectorHelper": true,
+ "dwarvenHud": {
+ "enabledCommissions": true,
+ "enabledPowder": true,
+ "style": "SIMPLE",
+ "x": 10,
+ "y": 10,
+ "powderX": 10,
+ "powderY": 70
+ },
+ "crystalsHud": {
+ "enabled": true,
+ "showLocations": true,
+ "locationSize": 8,
+ "x": 10,
+ "y": 130,
+ "mapScaling": 1.0
+ },
+ "crystalsWaypoints": {
+ "enabled": true,
+ "findInChat": true
+ }
+ },
+ "rift": {
+ "mirrorverseWaypoints": true,
+ "blobbercystGlow": true,
+ "enigmaSoulWaypoints": false,
+ "highlightFoundEnigmaSouls": true,
+ "mcGrubberStacks": 0
+ },
+ "end": {
+ "enableEnderNodeHelper": true,
+ "hudEnabled": true,
+ "zealotKillsEnabled": true,
+ "protectorLocationEnabled": true,
+ "waypoint": true,
+ "x": 10,
+ "y": 10
+ },
+ "spidersDen": {
+ "relics": {
+ "enableRelicsHelper": true,
+ "highlightFoundRelics": false
+ }
+ },
+ "garden": {
+ "farmingHud": {
+ "enableHud": true,
+ "x": 180,
+ "y": 0
+ },
+ "dicerTitlePrevent": true,
+ "visitorHelper": true,
+ "lockMouseTool": true,
+ "lockMouseGroundOnly": true
+ }
+ },
+ "slayer": {
+ "endermanSlayer": {
+ "enableYangGlyphsNotification": true,
+ "highlightBeacons": true,
+ "highlightNukekubiHeads": true
+ },
+ "vampireSlayer": {
+ "enableEffigyWaypoints": true,
+ "compactEffigyWaypoints": false,
+ "effigyUpdateFrequency": 5,
+ "enableHolyIceIndicator": true,
+ "holyIceIndicatorTickDelay": 10,
+ "holyIceUpdateFrequency": 5,
+ "enableHealingMelonIndicator": true,
+ "healingMelonHealthThreshold": 4.0,
+ "enableSteakStakeIndicator": true,
+ "steakStakeUpdateFrequency": 5,
+ "enableManiaIndicator": true,
+ "maniaUpdateFrequency": 5
+ }
+ },
+ "quickNav": {
+ "enableQuickNav": true,
+ "button1": {
+ "render": true,
+ "item": {
+ "itemName": "diamond_sword",
+ "count": 1,
+ "nbt": ""
+ },
+ "uiTitle": "Your Skills",
+ "clickEvent": "/skills"
+ },
+ "button2": {
+ "render": true,
+ "item": {
+ "itemName": "painting",
+ "count": 1,
+ "nbt": ""
+ },
+ "uiTitle": "Collections",
+ "clickEvent": "/collection"
+ },
+ "button3": {
+ "render": true,
+ "item": {
+ "itemName": "bone",
+ "count": 1,
+ "nbt": ""
+ },
+ "uiTitle": "Pets(:? \\(\\d+\\/\\d+\\))?",
+ "clickEvent": "/pets"
+ },
+ "button4": {
+ "render": true,
+ "item": {
+ "itemName": "leather_chestplate",
+ "count": 1,
+ "nbt": "tag:{display:{color:8991416}}"
+ },
+ "uiTitle": "Wardrobe \\([12]/2\\)",
+ "clickEvent": "/wardrobe"
+ },
+ "button5": {
+ "render": true,
+ "item": {
+ "itemName": "player_head",
+ "count": 1,
+ "nbt": "tag:{SkullOwner:{Id:[I;-2081424676,-57521078,-2073572414,158072763],Properties:{textures:[{Value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0=\"}]}}}"
+ },
+ "uiTitle": "Sack of Sacks",
+ "clickEvent": "/sacks"
+ },
+ "button6": {
+ "render": true,
+ "item": {
+ "itemName": "ender_chest",
+ "count": 1,
+ "nbt": ""
+ },
+ "uiTitle": "Storage",
+ "clickEvent": "/storage"
+ },
+ "button7": {
+ "render": true,
+ "item": {
+ "itemName": "player_head",
+ "count": 1,
+ "nbt": "tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}"
+ },
+ "uiTitle": "none",
+ "clickEvent": "/hub"
+ },
+ "button8": {
+ "render": true,
+ "item": {
+ "itemName": "player_head",
+ "count": 1,
+ "nbt": "tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}"
+ },
+ "uiTitle": "none",
+ "clickEvent": "/warp dungeon_hub"
+ },
+ "button9": {
+ "render": true,
+ "item": {
+ "itemName": "player_head",
+ "count": 1,
+ "nbt": "tag:{SkullOwner:{Id:[I;-562285948,532499670,-1705302742,775653035],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0=\"}]}}}"
+ },
+ "uiTitle": "Visit prtl",
+ "clickEvent": "/visit prtl"
+ },
+ "button10": {
+ "render": true,
+ "item": {
+ "itemName": "enchanting_table",
+ "count": 1,
+ "nbt": ""
+ },
+ "uiTitle": "Enchant Item",
+ "clickEvent": "/etable"
+ },
+ "button11": {
+ "render": true,
+ "item": {
+ "itemName": "anvil",
+ "count": 1,
+ "nbt": ""
+ },
+ "uiTitle": "Anvil",
+ "clickEvent": "/anvil"
+ },
+ "button12": {
+ "render": true,
+ "item": {
+ "itemName": "crafting_table",
+ "count": 1,
+ "nbt": ""
+ },
+ "uiTitle": "Craft Item",
+ "clickEvent": "/craft"
+ }
+ },
+ "messages": {
+ "hideAbility": "ACTION_BAR",
+ "hideHeal": "PASS",
+ "hideAOTE": "ACTION_BAR",
+ "hideImplosion": "PASS",
+ "hideMoltenWave": "PASS",
+ "hideAds": "FILTER",
+ "hideTeleportPad": "PASS",
+ "hideCombo": "PASS",
+ "hideAutopet": "PASS",
+ "hideShowOff": "FILTER",
+ "hideToggleSkyMall": "PASS",
+ "hideMimicKill": "PASS",
+ "hideDeath": "PASS",
+ "hideMana": false,
+ "hideDicer": "PASS",
+ "chatRuleConfig": {
+ "announcementLength": 60,
+ "announcementScale": 3
+ }
+ },
+ "richPresence": {
+ "enableRichPresence": false,
+ "info": "LOCATION",
+ "cycleMode": false,
+ "customMessage": "Playing Skyblock"
+ }
+} \ No newline at end of file
diff --git a/src/test/resources/assets/skyblocker/config/skyblocker-v2.json b/src/test/resources/assets/skyblocker/config/skyblocker-v2.json
new file mode 100644
index 00000000..e8489a5d
--- /dev/null
+++ b/src/test/resources/assets/skyblocker/config/skyblocker-v2.json
@@ -0,0 +1,573 @@
+{
+ "version": 2,
+ "general": {
+ "enableTips": true,
+ "acceptReparty": true,
+ "shortcuts": {
+ "enableShortcuts": true,
+ "enableCommandShortcuts": true,
+ "enableCommandArgShortcuts": true
+ },
+ "quiverWarning": {
+ "enableQuiverWarning": true,
+ "enableQuiverWarningInDungeons": true,
+ "enableQuiverWarningAfterDungeon": true
+ },
+ "itemList": {
+ "enableItemList": true
+ },
+ "itemTooltip": {
+ "enableNPCPrice": true,
+ "enableMotesPrice": true,
+ "enableAvgBIN": true,
+ "avg": "THREE_DAY",
+ "enableLowestBIN": true,
+ "enableBazaarPrice": true,
+ "enableObtainedDate": true,
+ "enableMuseumInfo": true,
+ "enableExoticTooltip": true,
+ "enableAccessoriesHelper": true,
+ "dungeonQuality": true
+ },
+ "itemInfoDisplay": {
+ "attributeShardInfo": true,
+ "itemRarityBackgrounds": true,
+ "itemRarityBackgroundStyle": "CIRCULAR",
+ "itemRarityBackgroundsOpacity": 0.5
+ },
+ "itemProtection": {
+ "slotLockStyle": "FANCY"
+ },
+ "wikiLookup": {
+ "enableWikiLookup": true,
+ "officialWiki": false
+ },
+ "specialEffects": {
+ "rareDungeonDropEffects": true
+ },
+ "hitbox": {
+ "oldFarmlandHitbox": true,
+ "oldLeverHitbox": false
+ },
+ "lockedSlots": [
+ 3,
+ 8,
+ 0
+ ],
+ "protectedItems": [
+ "10f84455-c063-4e3e-b6eb-cc485fe74a06",
+ "64e491fa-64ed-4abf-829a-940e1fb295d2",
+ "23a619b6-f487-419e-8598-10b8f21dc80a",
+ "416aa42d-27d4-4284-913f-d03fa1f528c3",
+ "4e2a7f16-7d93-4069-bfbd-a8ff8e42f285",
+ "63ec4f84-2005-45e5-a4b7-0c9ae84dc612",
+ "aba2b017-91e8-4263-ae9e-5a10f8dc91fe",
+ "f1391873-5126-496d-b0fe-c861faeaf681",
+ "bb84cfea-3fdd-4579-b5fa-907cd8123593",
+ "a604f533-49f6-4190-b971-08f725231229",
+ "333cba8d-3136-4494-9fa0-40dff482ef66",
+ "91f49338-fc0b-4601-a704-aabc44853318",
+ "d76e5eca-be47-4d0b-973c-3ec30fd176dc",
+ "301c58a7-9b8a-4c4e-9e8d-3e8a9471a916",
+ "c8ab629e-fc5b-4af0-8053-acb155411c9b",
+ "333a079f-93d4-48f1-af9f-862b1ecc417c",
+ "0843d8b0-b6a6-47fc-afde-8eee8c5455d9",
+ "8bcf2a99-68e6-4646-ac04-4c72e55c6818",
+ "214333db-d71c-4080-8487-00b4666bb9a4",
+ "1eba38b5-e0f6-4862-bf19-e2df33e21ce4",
+ "6b42c77f-1891-43a4-a9ea-4454e299a059",
+ "71ce1812-d1fd-4e71-98ad-8b863759a82f",
+ "253489d9-49d0-46c8-b3f8-b70c3663c5d2",
+ "6033eff2-879d-494f-8dae-696fc8a3c82e",
+ "5f6090c8-cbc4-4030-97ac-5018c151cc15",
+ "64162e02-91fb-4ec3-a57c-866436a99a7a",
+ "a299c718-c66b-4de5-9747-71c172ac4601",
+ "e21172ce-0a41-426a-a445-84a28677cd65",
+ "4bfd23bf-ceb8-4951-874d-e61633e088d8",
+ "575ce2c9-251e-4435-9020-de1a2e24b1d0",
+ "b06b8fe2-470a-43f3-b844-658472f20996",
+ "e54dea3d-ca45-451e-9ae4-0e5fb87e97b2",
+ "67b47348-da46-4257-833a-dd23fb074cc6",
+ "9f7597ec-c3c9-46df-9f36-8e76c8745a9d",
+ "e297dcef-2f3d-4fb3-9781-aaff6017178c",
+ "ad9a598d-e5c9-4293-92c8-5e41bebe913c",
+ "fa51c660-b262-4826-bed8-6138236919cb"
+ ],
+ "customItemNames": {},
+ "customDyeColors": {},
+ "customArmorTrims": {
+ "f1391873-5126-496d-b0fe-c861faeaf681": {
+ "material": "minecraft:amethyst",
+ "pattern": "minecraft:raiser"
+ },
+ "81409da1-610c-445a-aba2-4c95a3cabbf2": {
+ "material": "minecraft:amethyst",
+ "pattern": "minecraft:coast"
+ }
+ },
+ "customAnimatedDyes": {
+ "f1391873-5126-496d-b0fe-c861faeaf681": {
+ "color1": 16711680,
+ "color2": 11154282,
+ "samples": 10,
+ "cycleBack": true,
+ "tickDelay": 4
+ }
+ }
+ },
+ "uiAndVisuals": {
+ "compactorDeletorPreview": true,
+ "dontStripSkinAlphaValues": true,
+ "backpackPreviewWithoutShift": true,
+ "fancyCraftingTable": true,
+ "hideStatusEffectOverlay": false,
+ "chestValue": {
+ "enableChestValue": true,
+ "color": "DARK_GREEN",
+ "incompleteColor": "BLUE"
+ },
+ "itemCooldown": {
+ "enableItemCooldowns": true
+ },
+ "titleContainer": {
+ "titleContainerScale": 100.0,
+ "x": 482,
+ "y": 170,
+ "direction": "VERTICAL",
+ "alignment": "MIDDLE"
+ },
+ "tabHud": {
+ "tabHudEnabled": true,
+ "tabHudScale": 100,
+ "enableHudBackground": true,
+ "plainPlayerNames": false,
+ "nameSorting": "ALPHABETICAL"
+ },
+ "fancyAuctionHouse": {
+ "enabled": true,
+ "highlightCheapBIN": true
+ },
+ "bars": {
+ "enableBars": true,
+ "barPositions": {
+ "healthBarPosition": "LAYER1",
+ "manaBarPosition": "LAYER1",
+ "defenceBarPosition": "RIGHT",
+ "experienceBarPosition": "RIGHT"
+ }
+ },
+ "waypoints": {
+ "enableWaypoints": true,
+ "waypointType": "WAYPOINT"
+ },
+ "teleportOverlay": {
+ "enableTeleportOverlays": true,
+ "enableWeirdTransmission": true,
+ "enableInstantTransmission": true,
+ "enableEtherTransmission": true,
+ "enableSinrecallTransmission": true,
+ "enableWitherImpact": true
+ },
+ "searchOverlay": {
+ "enableBazaar": true,
+ "enableAuctionHouse": true,
+ "keepPreviousSearches": false,
+ "maxSuggestions": 5,
+ "historyLength": 5,
+ "enableCommands": false,
+ "bazaarHistory": [
+ "Enchanted Snow Block",
+ "Recombobulator 3000",
+ "Enchanted Snow Block",
+ "Recombobulator 3000",
+ "Enchanted Snow Block"
+ ],
+ "auctionHistory": [
+ "God Potion",
+ "New Year Cake (Year 30",
+ "New Year Cake (Year 31",
+ "New Year Cake (Year 32",
+ "New Year Cake (Year 2"
+ ]
+ },
+ "flameOverlay": {
+ "flameHeight": 0,
+ "flameOpacity": 0
+ },
+ "hideEmptyTooltips": true
+ },
+ "helpers": {
+ "enableNewYearCakesHelper": true,
+ "mythologicalRitual": {
+ "enableMythologicalRitualHelper": true
+ },
+ "experiments": {
+ "enableChronomatronSolver": true,
+ "enableSuperpairsSolver": true,
+ "enableUltrasequencerSolver": true
+ },
+ "fishing": {
+ "enableFishingHelper": true,
+ "enableFishingTimer": false,
+ "changeTimerColor": true,
+ "fishingTimerScale": 1.0,
+ "hideOtherPlayersRods": false
+ },
+ "fairySouls": {
+ "enableFairySoulsHelper": true,
+ "highlightFoundSouls": false,
+ "highlightOnlyNearbySouls": false
+ }
+ },
+ "dungeons": {
+ "fancyPartyFinder": true,
+ "croesusHelper": true,
+ "playerSecretsTracker": true,
+ "starredMobGlow": true,
+ "starredMobBoundingBoxes": true,
+ "allowDroppingProtectedItems": false,
+ "dungeonMap": {
+ "enableMap": true,
+ "mapScaling": 1.0,
+ "mapX": 2,
+ "mapY": 2
+ },
+ "puzzleSolvers": {
+ "solveThreeWeirdos": true,
+ "blazeSolver": true,
+ "creeperSolver": true,
+ "solveTrivia": true,
+ "solveTicTacToe": true,
+ "solveWaterboard": true,
+ "solveBoulder": true,
+ "solveIceFill": true,
+ "solveSilverfish": true
+ },
+ "theProfessor": {
+ "fireFreezeStaffTimer": true,
+ "floor3GuardianHealthDisplay": true
+ },
+ "livid": {
+ "enableLividColorGlow": true,
+ "enableLividColorText": true,
+ "enableLividColorTitle": true,
+ "lividColorText": "[color] is sus"
+ },
+ "terminals": {
+ "solveColor": true,
+ "solveOrder": true,
+ "solveStartsWith": true
+ },
+ "secretWaypoints": {
+ "enableRoomMatching": true,
+ "enableSecretWaypoints": true,
+ "waypointType": "OUTLINED_HIGHLIGHT",
+ "showSecretText": true,
+ "enableEntranceWaypoints": true,
+ "enableSuperboomWaypoints": true,
+ "enableChestWaypoints": true,
+ "enableItemWaypoints": true,
+ "enableBatWaypoints": true,
+ "enableWitherWaypoints": true,
+ "enableLeverWaypoints": true,
+ "enableFairySoulWaypoints": true,
+ "enableStonkWaypoints": false,
+ "enableAotvWaypoints": true,
+ "enablePearlWaypoints": true,
+ "enableDefaultWaypoints": true
+ },
+ "mimicMessage": {
+ "sendMimicMessage": true,
+ "mimicMessage": "Mimic dead!"
+ },
+ "doorHighlight": {
+ "enableDoorHighlight": true,
+ "doorHighlightType": "OUTLINED_HIGHLIGHT"
+ },
+ "dungeonScore": {
+ "enableDungeonScore270Message": false,
+ "enableDungeonScore270Title": false,
+ "enableDungeonScore270Sound": false,
+ "dungeonScore270Message": "270 Score Reached!",
+ "enableDungeonScore300Message": true,
+ "enableDungeonScore300Title": true,
+ "enableDungeonScore300Sound": true,
+ "dungeonScore300Message": "[做得好] 300 Social Credit has been added to your account. Enjoy your boss fight.",
+ "enableDungeonCryptsMessage": true,
+ "dungeonCryptsMessageThreshold": 250,
+ "dungeonCryptsMessage": "We only have [crypts] crypts out of 5, we need more!",
+ "enableScoreHUD": true,
+ "scoreX": 25,
+ "scoreY": 134,
+ "scoreScaling": 1.0
+ },
+ "dungeonChestProfit": {
+ "enableProfitCalculator": true,
+ "includeKismet": true,
+ "includeEssence": false,
+ "croesusProfit": true,
+ "neutralThreshold": 1000,
+ "neutralColor": "DARK_GRAY",
+ "profitColor": "DARK_GREEN",
+ "lossColor": "RED",
+ "incompleteColor": "BLUE"
+ }
+ },
+ "crimsonIsle": {
+ "kuudra": {
+ "supplyWaypoints": true,
+ "fuelWaypoints": true,
+ "suppliesAndFuelWaypointType": "WAYPOINT",
+ "ballistaBuildWaypoints": true,
+ "safeSpotWaypoints": true,
+ "pearlWaypoints": true,
+ "noArrowPoisonWarning": true,
+ "arrowPoisonThreshold": 16
+ }
+ },
+ "mining": {
+ "enableDrillFuel": true,
+ "dwarvenMines": {
+ "solveFetchur": true,
+ "solvePuzzler": true
+ },
+ "dwarvenHud": {
+ "enabledCommissions": true,
+ "enabledPowder": true,
+ "style": "SIMPLE",
+ "commissionsX": 10,
+ "commissionsY": 10,
+ "powderX": 10,
+ "powderY": 70
+ },
+ "crystalsHud": {
+ "enabled": true,
+ "showLocations": true,
+ "locationSize": 8,
+ "x": 10,
+ "y": 130,
+ "mapScaling": 1.0
+ },
+ "crystalsWaypoints": {
+ "enabled": true,
+ "findInChat": true
+ },
+ "crystalHollows": {
+ "metalDetectorHelper": true
+ }
+ },
+ "farming": {
+ "garden": {
+ "farmingHud": {
+ "enableHud": true,
+ "x": 180,
+ "y": 0
+ },
+ "dicerTitlePrevent": true,
+ "visitorHelper": true,
+ "lockMouseTool": true,
+ "lockMouseGroundOnly": true
+ }
+ },
+ "otherLocations": {
+ "barn": {
+ "solveHungryHiker": true,
+ "solveTreasureHunter": true
+ },
+ "rift": {
+ "mirrorverseWaypoints": true,
+ "blobbercystGlow": true,
+ "enigmaSoulWaypoints": false,
+ "highlightFoundEnigmaSouls": true,
+ "mcGrubberStacks": 0
+ },
+ "end": {
+ "enableEnderNodeHelper": true,
+ "hudEnabled": true,
+ "zealotKillsEnabled": true,
+ "protectorLocationEnabled": true,
+ "waypoint": true,
+ "x": 10,
+ "y": 10
+ },
+ "spidersDen": {
+ "relics": {
+ "enableRelicsHelper": true,
+ "highlightFoundRelics": false
+ }
+ }
+ },
+ "slayers": {
+ "endermanSlayer": {
+ "enableYangGlyphsNotification": true,
+ "highlightBeacons": true,
+ "highlightNukekubiHeads": true
+ },
+ "vampireSlayer": {
+ "enableEffigyWaypoints": true,
+ "compactEffigyWaypoints": false,
+ "effigyUpdateFrequency": 5,
+ "enableHolyIceIndicator": true,
+ "holyIceIndicatorTickDelay": 10,
+ "holyIceUpdateFrequency": 5,
+ "enableHealingMelonIndicator": true,
+ "healingMelonHealthThreshold": 4.0,
+ "enableSteakStakeIndicator": true,
+ "steakStakeUpdateFrequency": 5,
+ "enableManiaIndicator": true,
+ "maniaUpdateFrequency": 5
+ }
+ },
+ "chat": {
+ "hideAbility": "ACTION_BAR",
+ "hideHeal": "PASS",
+ "hideAOTE": "ACTION_BAR",
+ "hideImplosion": "PASS",
+ "hideMoltenWave": "PASS",
+ "hideAds": "FILTER",
+ "hideTeleportPad": "PASS",
+ "hideCombo": "PASS",
+ "hideAutopet": "PASS",
+ "hideShowOff": "FILTER",
+ "hideToggleSkyMall": "PASS",
+ "hideMimicKill": "PASS",
+ "hideDeath": "PASS",
+ "hideMana": false,
+ "hideDicer": "PASS",
+ "chatRuleConfig": {
+ "announcementLength": 60,
+ "announcementScale": 3
+ }
+ },
+ "quickNav": {
+ "enableQuickNav": true,
+ "button1": {
+ "render": true,
+ "uiTitle": "Your Skills",
+ "clickEvent": "/skills",
+ "item": {
+ "id": "diamond_sword",
+ "count": 1,
+ "components": "[]"
+ }
+ },
+ "button2": {
+ "render": true,
+ "uiTitle": "Collections",
+ "clickEvent": "/collection",
+ "item": {
+ "id": "painting",
+ "count": 1,
+ "components": "[]"
+ }
+ },
+ "button3": {
+ "render": true,
+ "uiTitle": "Pets(:? \\(\\d+\\/\\d+\\))?",
+ "clickEvent": "/pets",
+ "item": {
+ "id": "bone",
+ "count": 1,
+ "components": "[]"
+ }
+ },
+ "button4": {
+ "render": true,
+ "uiTitle": "Wardrobe \\([12]/2\\)",
+ "clickEvent": "/wardrobe",
+ "item": {
+ "id": "leather_chestplate",
+ "count": 1,
+ "components": "[minecraft:dyed_color\u003d{rgb:8991416}]"
+ }
+ },
+ "button5": {
+ "render": true,
+ "uiTitle": "Sack of Sacks",
+ "clickEvent": "/sacks",
+ "item": {
+ "id": "player_head",
+ "count": 1,
+ "components": "[minecraft:profile\u003d{id:[I;-2081424676,-57521078,-2073572414,158072763],name:\"\",properties:[{name:\"textures\",value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0\u003d\"}]}]"
+ }
+ },
+ "button6": {
+ "render": true,
+ "uiTitle": "Storage",
+ "clickEvent": "/storage",
+ "item": {
+ "id": "ender_chest",
+ "count": 1,
+ "components": "[]"
+ }
+ },
+ "button7": {
+ "render": true,
+ "uiTitle": "none",
+ "clickEvent": "/hub",
+ "item": {
+ "id": "player_head",
+ "count": 1,
+ "components": "[minecraft:profile\u003d{id:[I;-300151517,-631415889,-1193921967,-1821784279],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0\u003d\"}]}]"
+ }
+ },
+ "button8": {
+ "render": true,
+ "uiTitle": "none",
+ "clickEvent": "/warp dungeon_hub",
+ "item": {
+ "id": "player_head",
+ "count": 1,
+ "components": "[minecraft:profile\u003d{id:[I;1605800870,415127827,-1236127084,15358548],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0\u003d\"}]}]"
+ }
+ },
+ "button9": {
+ "render": true,
+ "uiTitle": "Visit prtl",
+ "clickEvent": "/visit prtl",
+ "item": {
+ "id": "player_head",
+ "count": 1,
+ "components": "[minecraft:profile\u003d{id:[I;-562285948,532499670,-1705302742,775653035],name:\"\",properties:[{name:\"textures\",value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0\u003d\"}]}]"
+ }
+ },
+ "button10": {
+ "render": true,
+ "uiTitle": "Enchant Item",
+ "clickEvent": "/etable",
+ "item": {
+ "id": "enchanting_table",
+ "count": 1,
+ "components": "[]"
+ }
+ },
+ "button11": {
+ "render": true,
+ "uiTitle": "Anvil",
+ "clickEvent": "/anvil",
+ "item": {
+ "id": "anvil",
+ "count": 1,
+ "components": "[]"
+ }
+ },
+ "button12": {
+ "render": true,
+ "uiTitle": "Craft Item",
+ "clickEvent": "/craft",
+ "item": {
+ "id": "crafting_table",
+ "count": 1,
+ "components": "[]"
+ }
+ }
+ },
+ "misc": {
+ "richPresence": {
+ "enableRichPresence": false,
+ "info": "LOCATION",
+ "cycleMode": false,
+ "customMessage": "Playing Skyblock"
+ }
+ }
+} \ No newline at end of file