diff options
| author | Aaron <51387595+AzureAaron@users.noreply.github.com> | 2024-03-02 15:16:27 -0500 | 
|---|---|---|
| committer | Aaron <51387595+AzureAaron@users.noreply.github.com> | 2024-04-26 16:23:20 -0400 | 
| commit | 4e2924407645b04c30d4a2823a1d9d0983c2c790 (patch) | |
| tree | ee6d4ba29f50d3cdd76216fee0193dad07752627 | |
| parent | cc00542e78fd87e0b554ab2a74d1cb193feabbb2 (diff) | |
| download | Skyblocker-4e2924407645b04c30d4a2823a1d9d0983c2c790.tar.gz Skyblocker-4e2924407645b04c30d4a2823a1d9d0983c2c790.tar.bz2 Skyblocker-4e2924407645b04c30d4a2823a1d9d0983c2c790.zip | |
24w09a
33 files changed, 387 insertions, 286 deletions
| diff --git a/build.gradle b/build.gradle index d7b37bfe..d5bb8008 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,12 @@ dependencies {  	testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"  	// To change the versions see the gradle.properties file  	minecraft "com.mojang:minecraft:${project.minecraft_version}" -	mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" +	//Layered Yarn & Mojmap - used to fill in intermediary names +	mappings loom.layered { +		//Using Mojmap breaks runClient, so uncomment only for snapshots when temp mappings are needed +		//officialMojangMappings() +		mappings("net.fabricmc:yarn:${project.yarn_mappings}:v2") +	}  	modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"  	// Fabric API diff --git a/gradle.properties b/gradle.properties index 9543d5cc..66191411 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,19 +3,19 @@ org.gradle.parallel=true  # Fabric Properties (https://fabricmc.net/versions.html)  ## 1.20 -minecraft_version=24w07a -yarn_mappings=24w07a+build.4 +minecraft_version=24w09a +yarn_mappings=24w09a+build.5  loader_version=0.15.7  #Fabric api  ## 1.20 -fabric_api_version=0.96.2+1.20.5 +fabric_api_version=0.96.5+1.20.5  # Minecraft Mods  ## YACL (https://github.com/isXander/YetAnotherConfigLib) -yacl_version=3.3.2+1.20.4 +yacl_version=3.3.3+1.20.4+update.1.20.5-SNAPSHOT+update.1.20.5-SNAPSHOT  ## Mod Menu (https://modrinth.com/mod/modmenu/versions) -mod_menu_version = 9.0.0-alpha.3 +mod_menu_version = 10.0.0-alpha.1  ## REI (https://modrinth.com/mod/rei/versions?l=fabric)  rei_version = 13.0.666  ## EMI (https://modrinth.com/mod/emi/versions) diff --git a/src/main/java/de/hysky/skyblocker/debug/Debug.java b/src/main/java/de/hysky/skyblocker/debug/Debug.java index 5a9d221b..31823ab0 100644 --- a/src/main/java/de/hysky/skyblocker/debug/Debug.java +++ b/src/main/java/de/hysky/skyblocker/debug/Debug.java @@ -23,7 +23,7 @@ public class Debug {  			SnapshotDebug.init();  			ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("debug")  					.then(dumpPlayersCommand()) -					.then(ItemUtils.dumpHeldItemNbtCommand()) +					.then(ItemUtils.dumpHeldItemCommand())  			)));  		}  	} diff --git a/src/main/java/de/hysky/skyblocker/mixin/ArmorTrimMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ArmorTrimMixin.java deleted file mode 100644 index e13c41ee..00000000 --- a/src/main/java/de/hysky/skyblocker/mixin/ArmorTrimMixin.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.hysky.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import com.llamalad7.mixinextras.sugar.Local; -import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.skyblock.item.CustomArmorTrims; -import de.hysky.skyblocker.utils.ItemUtils; -import de.hysky.skyblocker.utils.Utils; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.item.ItemStack; -import net.minecraft.item.trim.ArmorTrim; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.Optional; - -@Mixin(ArmorTrim.class) -public class ArmorTrimMixin { - -	@ModifyReturnValue(method = "getTrim", at = @At("RETURN")) -	private static Optional<ArmorTrim> skyblocker$customArmorTrims(@SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional<ArmorTrim> original, @Local(argsOnly = true) ItemStack stack) { -		if (Utils.isOnSkyblock()) { -			Object2ObjectOpenHashMap<String, CustomArmorTrims.ArmorTrimId> customTrims = SkyblockerConfigManager.get().general.customArmorTrims; -			String itemUuid = ItemUtils.getItemUuid(stack); - -			if (customTrims.containsKey(itemUuid)) { -				CustomArmorTrims.ArmorTrimId trimKey = customTrims.get(itemUuid); -				return CustomArmorTrims.TRIMS_CACHE.getOrDefault(trimKey, original); -			} -		} - -		return original; -	} -} diff --git a/src/main/java/de/hysky/skyblocker/mixin/ComponentHolderMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ComponentHolderMixin.java new file mode 100644 index 00000000..c8c57d6d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/ComponentHolderMixin.java @@ -0,0 +1,39 @@ +package de.hysky.skyblocker.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.CustomArmorTrims; +import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.Utils; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.component.ComponentHolder; +import net.minecraft.component.DataComponentType; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.item.ItemStack; +import net.minecraft.item.trim.ArmorTrim; + +@Mixin(ComponentHolder.class) +public interface ComponentHolderMixin { + +	@SuppressWarnings("unchecked") +	@ModifyReturnValue(method = "get", at = @At("RETURN")) +	private <T> T skyblocker$customArmorTrims(T original, DataComponentType<? extends T> dataComponentType) { +		if (Utils.isOnSkyblock() && ((Object) this) instanceof ItemStack stack) { +			if (dataComponentType == DataComponentTypes.TRIM) { +				Object2ObjectOpenHashMap<String, CustomArmorTrims.ArmorTrimId> customTrims = SkyblockerConfigManager.get().general.customArmorTrims; +				String itemUuid = ItemUtils.getItemUuid(stack); + +				if (customTrims.containsKey(itemUuid)) { +					CustomArmorTrims.ArmorTrimId trimKey = customTrims.get(itemUuid); +					return (T) CustomArmorTrims.TRIMS_CACHE.getOrDefault(trimKey, (ArmorTrim) original); +				} +			} +		} + +		return original; +	} +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/DyeableItemMixin.java b/src/main/java/de/hysky/skyblocker/mixin/DyedColorComponentMixin.java index b20b58b5..2682fc6d 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/DyeableItemMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/DyedColorComponentMixin.java @@ -1,27 +1,32 @@  package de.hysky.skyblocker.mixin;  import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.llamalad7.mixinextras.sugar.Local; +  import de.hysky.skyblocker.config.SkyblockerConfigManager;  import de.hysky.skyblocker.skyblock.item.CustomArmorAnimatedDyes;  import de.hysky.skyblocker.utils.ItemUtils;  import de.hysky.skyblocker.utils.Utils; -import net.minecraft.item.DyeableItem; +import net.minecraft.component.type.DyedColorComponent;  import net.minecraft.item.ItemStack; +import net.minecraft.util.math.ColorHelper; +  import org.spongepowered.asm.mixin.Mixin;  import org.spongepowered.asm.mixin.injection.At; -@Mixin(DyeableItem.class) -public interface DyeableItemMixin { +@Mixin(DyedColorComponent.class) +public record DyedColorComponentMixin() { +  	@ModifyReturnValue(method = "getColor", at = @At("RETURN")) -	private static int skyblocker$customDyeColor(int originalColor, ItemStack stack) { +	private static int skyblocker$customDyeColor(int originalColor, @Local(argsOnly = true) ItemStack stack) {  		if (Utils.isOnSkyblock()) {  			String itemUuid = ItemUtils.getItemUuid(stack);  			if (SkyblockerConfigManager.get().general.customAnimatedDyes.containsKey(itemUuid)) { -				return CustomArmorAnimatedDyes.animateColorTransition(SkyblockerConfigManager.get().general.customAnimatedDyes.get(itemUuid)); +				return ColorHelper.Argb.fullAlpha(CustomArmorAnimatedDyes.animateColorTransition(SkyblockerConfigManager.get().general.customAnimatedDyes.get(itemUuid)));  			} -			return SkyblockerConfigManager.get().general.customDyeColors.getOrDefault(itemUuid, originalColor); +			return ColorHelper.Argb.fullAlpha(SkyblockerConfigManager.get().general.customDyeColors.getOrDefault(itemUuid, originalColor));  		}  		return originalColor; diff --git a/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java index 7d2f849e..73a0bfdb 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java @@ -205,7 +205,7 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen              // Prevent selling to NPC shops              ItemStack sellStack = this.handler.slots.get(49).getStack(); -            if (sellStack.getName().getString().equals("Sell Item") || ItemUtils.getNbtTooltip(sellStack, text -> text.contains("buyback")) != null) { +            if (sellStack.getName().getString().equals("Sell Item") || ItemUtils.getLoreLineIf(sellStack, text -> text.contains("buyback")) != null) {                  if (slotId != 49 && ItemProtection.isItemProtected(stack)) {                      ci.cancel();                      return; diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/ItemStackAccessor.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/ItemStackAccessor.java deleted file mode 100644 index e3bd69aa..00000000 --- a/src/main/java/de/hysky/skyblocker/mixin/accessor/ItemStackAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.hysky.skyblocker.mixin.accessor; - -import net.minecraft.item.ItemStack; -import net.minecraft.text.Style; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(ItemStack.class) -public interface ItemStackAccessor { -    @Accessor -    static Style getLORE_STYLE() { -        throw new UnsupportedOperationException(); -    } -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java index 01422770..5bd44707 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java @@ -1,8 +1,10 @@  package de.hysky.skyblocker.skyblock.dungeon;  import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.ItemUtils;  import de.hysky.skyblocker.utils.render.gui.ColorHighlight;  import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import net.minecraft.component.DataComponentTypes;  import net.minecraft.item.ItemStack;  import java.util.ArrayList; @@ -25,10 +27,10 @@ public class CroesusHelper extends ContainerSolver {          List<ColorHighlight> highlights = new ArrayList<>();          for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) {              ItemStack stack = entry.getValue(); -            if (stack != null && stack.getNbt() != null) { -                if (stack.getNbt().toString().contains("Opened Chest:")) { +            if (stack != null && stack.contains(DataComponentTypes.LORE)) { +                if (ItemUtils.getLoreLineIf(stack, s -> s.contains("Opened Chest:")) != null) {                      highlights.add(ColorHighlight.gray(entry.getKey())); -                } else if (stack.getNbt().toString().contains("No more Chests to open!")) { +                } else if (ItemUtils.getLoreLineIf(stack, s -> s.contains("No more Chests to open!")) != null) {                      highlights.add(ColorHighlight.red(entry.getKey()));                  }              } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusProfit.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusProfit.java index ca166915..8933a447 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusProfit.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusProfit.java @@ -6,6 +6,7 @@ import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;  import de.hysky.skyblocker.utils.ItemUtils;  import de.hysky.skyblocker.utils.render.gui.ColorHighlight;  import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import net.minecraft.component.DataComponentTypes;  import net.minecraft.item.ItemStack;  import net.minecraft.text.Text;  import org.jetbrains.annotations.NotNull; @@ -37,7 +38,7 @@ public class CroesusProfit extends ContainerSolver {          for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) {              ItemStack stack = entry.getValue(); -            if (stack != null && stack.getNbt() != null && stack.getName().toString().contains("Chest")) { +            if (stack != null && stack.contains(DataComponentTypes.LORE) && ItemUtils.getLoreLineIf(stack, s -> s.contains("Chest")) != null) {                  long value = valueChest(stack);                  if (value > bestValue) {                      secondBestChest = bestChest; @@ -53,7 +54,7 @@ public class CroesusProfit extends ContainerSolver {          for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) {              ItemStack stack = entry.getValue(); -            if (stack != null && stack.getNbt() != null) { +            if (stack != null && stack.contains(DataComponentTypes.LORE)) {                  if (stack.equals(bestChest)) {                      highlights.add(ColorHighlight.green(entry.getKey()));                  } else if (stack.equals(secondBestChest) && secondBestValue > dungeonKeyPriceData) { @@ -71,7 +72,7 @@ public class CroesusProfit extends ContainerSolver {          List<String> chestItems = new ArrayList<>();          boolean processingContents = false; -        for (Text line : ItemUtils.getNbtTooltips(chest)) { +        for (Text line : ItemUtils.getLore(chest)) {              String lineString = line.getString();              if (lineString.contains("Contents")) {                  processingContents = true; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java index 878b7a35..7a6cdcd0 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java @@ -14,14 +14,16 @@ import net.minecraft.client.render.LightmapTextureManager;  import net.minecraft.client.render.MapRenderer;  import net.minecraft.client.render.VertexConsumerProvider;  import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.MapIdComponent;  import net.minecraft.item.FilledMapItem;  import net.minecraft.item.ItemStack;  import net.minecraft.item.Items;  import net.minecraft.item.map.MapState;  public class DungeonMap { -    private static final int DEFAULT_MAP_ID = 1024; -    private static Integer cachedMapId = null; +    private static final MapIdComponent DEFAULT_MAP_ID_COMPONENT = new MapIdComponent(1024); +    private static MapIdComponent cachedMapIdComponent = null;      public static void init() {      	HudRenderEvents.AFTER_MAIN_HUD.register((context, tickDelta) -> render(context)); @@ -35,11 +37,11 @@ public class DungeonMap {          ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset());      } -    private static void render(MatrixStack matrices) { +    public static void render(MatrixStack matrices) {          MinecraftClient client = MinecraftClient.getInstance();          if (client.player == null || client.world == null) return; -        int mapId = getMapId(client.player.getInventory().main.get(8)); +        MapIdComponent mapId = getMapIdComponent(client.player.getInventory().main.get(8));          MapState state = FilledMapItem.getMapState(mapId, client.world);          if (state == null) return; @@ -58,13 +60,12 @@ public class DungeonMap {          matrices.pop();      } -    public static int getMapId(ItemStack stack) { -        if (stack.isOf(Items.FILLED_MAP)) { -            @SuppressWarnings("DataFlowIssue") -            int mapId = FilledMapItem.getMapId(stack); -            cachedMapId = mapId; -            return mapId; -        } else return cachedMapId != null ? cachedMapId : DEFAULT_MAP_ID; +    public static MapIdComponent getMapIdComponent(ItemStack stack) { +        if (stack.isOf(Items.FILLED_MAP) && stack.contains(DataComponentTypes.MAP_ID)) { +            MapIdComponent mapIdComponent = stack.get(DataComponentTypes.MAP_ID); +            cachedMapIdComponent = mapIdComponent; +            return mapIdComponent; +        } else return cachedMapIdComponent != null ? cachedMapIdComponent : DEFAULT_MAP_ID_COMPONENT;      }      private static void render(DrawContext context) { @@ -74,6 +75,6 @@ public class DungeonMap {      }      private static void reset() { -        cachedMapId = null; +        cachedMapIdComponent = null;      }  } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/OptionDropdownWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/OptionDropdownWidget.java index 64e45283..19241d58 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/OptionDropdownWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/OptionDropdownWidget.java @@ -29,7 +29,6 @@ public class OptionDropdownWidget extends ElementListWidget<OptionDropdownWidget          this.screen = screen;          this.slotId = slotId;          setX(x); -        setRenderBackground(false);          setRenderHeader(true, 25);          this.name = name;          this.selectedOption = selectedOption; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyEntry.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyEntry.java index b53047d8..ab77d122 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyEntry.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyEntry.java @@ -1,6 +1,6 @@  package de.hysky.skyblocker.skyblock.dungeon.partyfinder; -import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.authlib.properties.PropertyMap;  import de.hysky.skyblocker.SkyblockerMod;  import de.hysky.skyblocker.mixin.accessor.SkullBlockEntityAccessor;  import net.minecraft.client.MinecraftClient; @@ -12,10 +12,10 @@ import net.minecraft.client.gui.Selectable;  import net.minecraft.client.gui.widget.ElementListWidget;  import net.minecraft.client.util.DefaultSkinHelper;  import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.ProfileComponent;  import net.minecraft.item.ItemStack;  import net.minecraft.item.Items; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.StringNbtReader;  import net.minecraft.text.Style;  import net.minecraft.text.Text;  import net.minecraft.text.TextColor; @@ -25,6 +25,8 @@ import net.minecraft.util.Identifier;  import java.util.Arrays;  import java.util.List;  import java.util.Objects; +import java.util.Optional; +import java.util.UUID;  import java.util.regex.Matcher;  import java.util.regex.Pattern; @@ -38,7 +40,7 @@ public class PartyEntry extends ElementListWidget.Entry<PartyEntry> {      String floor = "???";      String dungeon = "???";      String note = ""; -    NbtCompound floorSkullNBT = new NbtCompound(); +    PropertyMap floorSkullProperties = new PropertyMap();      Identifier partyLeaderSkin = DefaultSkinHelper.getTexture();      Player[] partyMembers = new Player[4]; @@ -87,14 +89,14 @@ public class PartyEntry extends ElementListWidget.Entry<PartyEntry> {                  if (PartyFinderScreen.floorIconsMaster == null || PartyFinderScreen.floorIconsNormal == null) continue;                  if (dungeon.contains("Master Mode")) {                      try { -                        floorSkullNBT = StringNbtReader.parse(PartyEntryListWidget.BASE_SKULL_NBT.replace("%TEXTURE%", PartyFinderScreen.floorIconsMaster.getOrDefault(floor.toLowerCase(), ""))); -                    } catch (CommandSyntaxException e) { +                        floorSkullProperties = PartyFinderScreen.floorIconsMaster.getOrDefault(floor.toLowerCase(), new PropertyMap()); +                    } catch (Exception e) {                          throw new RuntimeException(e);                      }                  } else {                      try { -                        floorSkullNBT = StringNbtReader.parse(PartyEntryListWidget.BASE_SKULL_NBT.replace("%TEXTURE%", PartyFinderScreen.floorIconsNormal.getOrDefault(floor.toLowerCase(), ""))); -                    } catch (CommandSyntaxException e) { +                    	floorSkullProperties = PartyFinderScreen.floorIconsNormal.getOrDefault(floor.toLowerCase(), new PropertyMap()); +                    } catch (Exception e) {                          throw new RuntimeException(e);                      }                  } @@ -226,7 +228,7 @@ public class PartyEntry extends ElementListWidget.Entry<PartyEntry> {              }          }          ItemStack stack = new ItemStack(Items.PLAYER_HEAD); -        stack.setNbt(floorSkullNBT); +        stack.set(DataComponentTypes.PROFILE, new ProfileComponent("SkyblockerCustomPF", Optional.of(UUID.randomUUID()), floorSkullProperties));          context.drawItem(stack, 317, 3);          int textWidth = textRenderer.getWidth(floor); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyFinderScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyFinderScreen.java index 16be2b67..98ab88c0 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyFinderScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyFinderScreen.java @@ -1,7 +1,10 @@  package de.hysky.skyblocker.skyblock.dungeon.partyfinder;  import com.google.gson.JsonObject; +import com.mojang.authlib.properties.PropertyMap; +  import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.utils.ItemUtils;  import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;  import net.minecraft.block.entity.SignBlockEntity;  import net.minecraft.client.MinecraftClient; @@ -99,8 +102,8 @@ public class PartyFinderScreen extends Screen {      private boolean waitingForServer = false; -    public static Map<String, String> floorIconsNormal = null; -    public static Map<String, String> floorIconsMaster = null; +    public static Map<String, PropertyMap> floorIconsNormal = null; +    public static Map<String, PropertyMap> floorIconsMaster = null;      public static void initClass() {          ClientLifecycleEvents.CLIENT_STARTED.register(client -> { @@ -110,8 +113,8 @@ public class PartyFinderScreen extends Screen {                  floorIconsMaster = new HashMap<>();                  try (BufferedReader skullTextureReader = client.getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/catacombs/floorskulls.json"))) {                      JsonObject json = SkyblockerMod.GSON.fromJson(skullTextureReader, JsonObject.class); -                    json.getAsJsonObject("normal").asMap().forEach((s, jsonElement) -> floorIconsNormal.put(s, jsonElement.getAsString())); -                    json.getAsJsonObject("master").asMap().forEach((s, jsonElement) -> floorIconsMaster.put(s, jsonElement.getAsString())); +                    json.getAsJsonObject("normal").asMap().forEach((s, tex) -> floorIconsNormal.put(s, ItemUtils.propertyMapWithTexture(tex.getAsString()))); +                    json.getAsJsonObject("master").asMap().forEach((s, tex) -> floorIconsMaster.put(s, ItemUtils.propertyMapWithTexture(tex.getAsString())));                      LOGGER.debug("[Skyblocker] Dungeons floor skull textures json loaded");                  } catch (Exception e) {                      LOGGER.error("[Skyblocker] Failed to load dungeons floor skull textures json", e); @@ -136,7 +139,6 @@ public class PartyFinderScreen extends Screen {          int widget_height = (int) (this.height * 0.8);          int entryListTopY = Math.max(43, (int) (height * 0.1));          this.partyEntryListWidget = new PartyEntryListWidget(client, width, widget_height, entryListTopY, 68); -        partyEntryListWidget.setRenderBackground(false);          // Search field          this.searchField = new TextFieldWidget(textRenderer, partyEntryListWidget.getRowLeft() + 12, entryListTopY - 12, partyEntryListWidget.getRowWidth() - 12 * 3 - 6, 12, Text.literal("Search...")); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java index c90f1134..8bb22e7e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java @@ -40,6 +40,7 @@ import net.minecraft.command.CommandSource;  import net.minecraft.command.argument.BlockPosArgumentType;  import net.minecraft.command.argument.PosArgument;  import net.minecraft.command.argument.TextArgumentType; +import net.minecraft.component.DataComponentTypes;  import net.minecraft.entity.Entity;  import net.minecraft.entity.ItemEntity;  import net.minecraft.entity.LivingEntity; @@ -462,7 +463,7 @@ public class DungeonManager {                  context.getSource().sendError(Constants.PREFIX.get().append("§cFailed to get dungeon map."));                  return Command.SINGLE_SUCCESS;              } -            MapState map = FilledMapItem.getMapState(FilledMapItem.getMapId(stack), client.world); +            MapState map = FilledMapItem.getMapState(stack.get(DataComponentTypes.MAP_ID), client.world);              if (map == null) {                  context.getSource().sendError(Constants.PREFIX.get().append("§cFailed to get dungeon map state."));                  return Command.SINGLE_SUCCESS; @@ -539,7 +540,7 @@ public class DungeonManager {              physicalEntrancePos = DungeonMapUtils.getPhysicalRoomPos(playerPos);              currentRoom = newRoom(Room.Type.ENTRANCE, physicalEntrancePos);          } -        MapState map = FilledMapItem.getMapState(DungeonMap.getMapId(client.player.getInventory().main.get(8)), client.world); +        MapState map = FilledMapItem.getMapState(DungeonMap.getMapIdComponent(client.player.getInventory().main.get(8)), client.world);          if (map == null) {              return;          } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudWidget.java index 30de9a48..10dc2b5c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudWidget.java @@ -4,6 +4,8 @@ import de.hysky.skyblocker.config.SkyblockerConfigManager;  import de.hysky.skyblocker.skyblock.tabhud.widget.Widget;  import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;  import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.ProfileComponent;  import net.minecraft.enchantment.Enchantments;  import net.minecraft.item.ItemStack;  import net.minecraft.item.Items; @@ -12,6 +14,9 @@ import net.minecraft.text.Text;  import net.minecraft.util.Formatting;  import java.text.NumberFormat; +import java.util.Optional; + +import com.mojang.authlib.properties.PropertyMap;  public class EndHudWidget extends Widget {      private static final MutableText TITLE = Text.literal("The End").formatted(Formatting.LIGHT_PURPLE, Formatting.BOLD); @@ -29,7 +34,7 @@ public class EndHudWidget extends Widget {      private static final ItemStack POPPY = new ItemStack(Items.POPPY);      static { -        ENDERMAN_HEAD.getOrCreateNbt().putString("SkullOwner", "MHF_Enderman"); +        ENDERMAN_HEAD.set(DataComponentTypes.PROFILE, new ProfileComponent("MHF_Enderman", Optional.empty(), new PropertyMap()));          POPPY.addEnchantment(Enchantments.INFINITY, 1);          INSTANCE.setX(SkyblockerConfigManager.get().locations.end.x); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java index 12ae468f..9650e39b 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java @@ -6,14 +6,15 @@ import de.hysky.skyblocker.skyblock.dungeon.LividColor;  import de.hysky.skyblocker.utils.SlayerUtils;  import de.hysky.skyblocker.utils.Utils;  import de.hysky.skyblocker.utils.render.culling.OcclusionCulling; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.ProfileComponent;  import net.minecraft.entity.Entity;  import net.minecraft.entity.decoration.ArmorStandEntity;  import net.minecraft.entity.mob.EndermanEntity;  import net.minecraft.entity.passive.BatEntity;  import net.minecraft.entity.player.PlayerEntity;  import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtElement; +import net.minecraft.item.Items;  import net.minecraft.predicate.entity.EntityPredicates;  import net.minecraft.util.Formatting;  import net.minecraft.util.math.Box; @@ -21,6 +22,8 @@ import net.minecraft.world.World;  import java.util.List; +import com.mojang.authlib.properties.Property; +  public class MobGlow {  	public static boolean shouldMobGlow(Entity entity) { @@ -114,19 +117,18 @@ public class MobGlow {  		for (ItemStack armorItem : entity.getArmorItems()) {  			// hacky way to check if an item is a player head w/o  			// some shenanigans -			if (!armorItem.toString().startsWith("1 player_head")) +			if (!armorItem.isOf(Items.PLAYER_HEAD))  				continue;  			// eb07594e2df273921a77c101d0bfdfa1115abed5b9b2029eb496ceba9bdbb4b3 is texture id for the nukekubi head,  			// compare against it to exclusively find armorstands that are nukekubi heads -			NbtCompound skullOwner = armorItem.getSubNbt("SkullOwner"); -			if (skullOwner != null) { +			ProfileComponent profile = armorItem.get(DataComponentTypes.PROFILE); +			if (profile != null) {  				// get the texture of the nukekubi head item itself and compare it -				String texture = skullOwner -						.getCompound("Properties") -						.getList("textures", NbtElement.COMPOUND_TYPE) -						.getCompound(0) -						.getString("Value"); +				String texture = profile.properties().get("textures").stream() +						.map(Property::value) +						.findFirst() +						.orElse("None");  				return texture.contains("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWIwNzU5NGUyZGYyNzM5MjFhNzdjMTAxZDBiZmRmYTExMTVhYmVkNWI5YjIwMjllYjQ5NmNlYmE5YmRiYjRiMyJ9fX0=");  			} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java index 95c72241..374e9e12 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/FarmingHud.java @@ -54,7 +54,7 @@ public class FarmingHud {                  }                  ItemStack stack = MinecraftClient.getInstance().player.getMainHandStack(); -                Matcher matcher = ItemUtils.getNbtTooltip(stack, FarmingHud.COUNTER); +                Matcher matcher = ItemUtils.getLoreLineIfMatch(stack, FarmingHud.COUNTER);                  if (matcher != null) {                      try {                          int count = NUMBER_FORMAT.parse(matcher.group("count")).intValue(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java index 6640d413..2805cb0e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java @@ -2,6 +2,7 @@ package de.hysky.skyblocker.skyblock.garden;  import de.hysky.skyblocker.config.SkyblockerConfigManager;  import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.utils.ItemUtils;  import de.hysky.skyblocker.utils.NEURepoManager;  import de.hysky.skyblocker.utils.Utils;  import de.hysky.skyblocker.utils.scheduler.MessageScheduler; @@ -12,14 +13,11 @@ import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;  import net.minecraft.client.font.TextRenderer;  import net.minecraft.client.gui.DrawContext;  import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.component.DataComponentTypes;  import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtElement; -import net.minecraft.nbt.NbtList;  import net.minecraft.screen.ScreenHandler;  import net.minecraft.screen.slot.Slot;  import net.minecraft.text.Text; -import net.minecraft.text.Text.Serialization;  import net.minecraft.util.Formatting;  import org.jetbrains.annotations.Nullable;  import org.slf4j.Logger; @@ -27,6 +25,7 @@ import org.slf4j.LoggerFactory;  import java.text.NumberFormat;  import java.util.HashMap; +import java.util.List;  import java.util.Locale;  import java.util.Map; @@ -85,30 +84,27 @@ public class VisitorHelper {      private static void processVisitorItem(String visitorName, ScreenHandler handler) {          ItemStack visitorItem = handler.getSlot(13).getStack(); -        if (visitorItem == null || !visitorItem.hasNbt() || !visitorItem.getNbt().asString().contains("Times Visited")) return; +        if (visitorItem == null || !visitorItem.contains(DataComponentTypes.LORE) || ItemUtils.getLoreLineIf(visitorItem, t -> t.contains("Times Visited")) == null) return;          ItemStack acceptButton = handler.getSlot(29).getStack();          if (acceptButton == null) return; -        NbtCompound acceptButtonNbt = acceptButton.getSubNbt("display"); -        if (acceptButtonNbt == null || !acceptButtonNbt.contains("Lore", NbtElement.LIST_TYPE)) return; -        processLore(visitorName, acceptButtonNbt.getList("Lore", NbtElement.STRING_TYPE)); +        processLore(visitorName, ItemUtils.getLore(acceptButton));      } -    private static void processLore(String visitorName, NbtList loreList) { +    private static void processLore(String visitorName, List<Text> loreList) {          boolean saveRequiredItems = false;          for (int i = 0; i < loreList.size(); i++) { -            String lore = loreList.getString(i); +            String lore = loreList.get(i).getString();              if (lore.contains("Items Required"))                  saveRequiredItems = true;              else if (lore.contains("Rewards"))                  break;              else if (saveRequiredItems) -                updateItemMap(visitorName, lore); +                updateItemMap(visitorName, loreList.get(i));          }      } -    private static void updateItemMap(String visitorName, String lore) { -        Text itemText = Serialization.fromJson(lore); -        String[] splitItemText = itemText.getString().split(" x"); +    private static void updateItemMap(String visitorName, Text lore) { +        String[] splitItemText = lore.getString().split(" x");          String itemName = splitItemText[0].trim();          if (itemName.isEmpty()) return;          try { @@ -117,7 +113,7 @@ public class VisitorHelper {              visitorMap.putIfAbsent(itemName, amount);              itemMap.putIfAbsent(visitorName, visitorMap);          } catch (Exception e) { -            LOGGER.error("[Skyblocker Visitor Helper] Failed to parse item: " + itemText.getString(), e); +            LOGGER.error("[Skyblocker Visitor Helper] Failed to parse item: " + lore.getString(), e);          }      } @@ -163,7 +159,7 @@ public class VisitorHelper {      }      private static void drawItemEntryWithHover(DrawContext context, TextRenderer textRenderer, @Nullable ItemStack stack, String itemName, int amount, int index, int mouseX, int mousseY) { -        Text text = stack != null ? Serialization.fromJson(stack.getSubNbt("display").getString("Name")).append(" x" + amount) : Text.literal(itemName + " x" + amount); +    	Text text = stack != null ? stack.getName().copy().append(" x" + amount) : Text.literal(itemName + " x" + amount);          drawTextWithOptionalUnderline(context, textRenderer, text, TEXT_START_X + TEXT_INDENT, TEXT_START_Y + (index * (LINE_SPACING + textRenderer.fontHeight)), mouseX, mousseY);          // drawItem adds 150 to the z, which puts our z at 350, above the item in the slot (250) and their text (300) and below the cursor stack (382) and their text (432)          if (stack != null) { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java index 639e98ed..97311220 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java @@ -12,8 +12,8 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;  import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;  import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;  import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.item.DyeableItem;  import net.minecraft.item.ItemStack; +import net.minecraft.registry.tag.ItemTags;  import net.minecraft.text.Text;  public class CustomArmorDyeColors { @@ -40,7 +40,7 @@ public class CustomArmorDyeColors {  		}  	if (Utils.isOnSkyblock() && heldItem != null) { -			if (heldItem.getItem() instanceof DyeableItem) { +			if (heldItem.isIn(ItemTags.DYEABLE)) {  				String itemUuid = ItemUtils.getItemUuid(heldItem);  				if (!itemUuid.isEmpty()) { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java index 3434f026..270622ff 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java @@ -34,11 +34,9 @@ import org.jetbrains.annotations.NotNull;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory; -import java.util.Optional; -  public class CustomArmorTrims {  	private static final Logger LOGGER = LoggerFactory.getLogger(CustomArmorTrims.class); -	public static final Object2ObjectOpenHashMap<ArmorTrimId, Optional<ArmorTrim>> TRIMS_CACHE = new Object2ObjectOpenHashMap<>(); +	public static final Object2ObjectOpenHashMap<ArmorTrimId, ArmorTrim> TRIMS_CACHE = new Object2ObjectOpenHashMap<>();  	private static boolean trimsInitialized = false;  	public static void init() { @@ -65,7 +63,7 @@ public class CustomArmorTrims {  					// Something went terribly wrong  					if (trim == null) throw new IllegalStateException("Trim shouldn't be null! [" + "\"" + material + "\",\"" + pattern + "\"]"); -					TRIMS_CACHE.put(new ArmorTrimId(material, pattern), Optional.of(trim)); +					TRIMS_CACHE.put(new ArmorTrimId(material, pattern), trim);  				}  			} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/BackpackPreview.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/BackpackPreview.java index 37de58e6..77cbc953 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/BackpackPreview.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/BackpackPreview.java @@ -1,6 +1,11 @@  package de.hysky.skyblocker.skyblock.item.tooltip; +import com.google.gson.JsonParser;  import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; +import com.mojang.serialization.codecs.RecordCodecBuilder; +  import de.hysky.skyblocker.SkyblockerMod;  import de.hysky.skyblocker.config.SkyblockerConfigManager;  import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds; @@ -15,18 +20,16 @@ import net.minecraft.client.util.math.MatrixStack;  import net.minecraft.inventory.Inventory;  import net.minecraft.inventory.SimpleInventory;  import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtInt; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NbtList;  import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory; +import java.io.BufferedReader; +import java.io.BufferedWriter;  import java.nio.file.Files;  import java.nio.file.Path; -import java.util.Objects; +import java.util.ArrayList; +import java.util.List;  import java.util.regex.Matcher;  import java.util.regex.Pattern; @@ -61,8 +64,13 @@ public class BackpackPreview {              String id = MinecraftClient.getInstance().getSession().getUuidOrNull().toString().replaceAll("-", "") + "/" + Utils.getProfileId();              if (!id.equals(loaded)) {                  saveDir = SkyblockerMod.CONFIG_DIR.resolve("backpack-preview/" + id); -                //noinspection ResultOfMethodCallIgnored -                saveDir.toFile().mkdirs(); + +                try { +                    Files.createDirectories(saveDir); +                } catch (Exception e) { +                    LOGGER.error("[Skyblocker] Failed to create the backpack preview save directory! Path: {}", saveDir, e); +                } +                  // load storage again because profile id changed                  loaded = id;                  loadStorages(); @@ -73,10 +81,10 @@ public class BackpackPreview {      private static void loadStorages() {          for (int index = 0; index < STORAGE_SIZE; ++index) {              storages[index] = null; -            Path storageFile = saveDir.resolve(index + ".nbt"); +            Path storageFile = saveDir.resolve(index + ".json");              if (Files.isRegularFile(storageFile)) { -                try { -                    storages[index] = Storage.fromNbt(Objects.requireNonNull(NbtIo.read(storageFile))); +                try (BufferedReader reader = Files.newBufferedReader(storageFile)) { +                    storages[index] = Storage.CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).result().orElseThrow();                  } catch (Exception e) {                      LOGGER.error("Failed to load backpack preview file: " + storageFile.getFileName().toString(), e);                  } @@ -93,11 +101,12 @@ public class BackpackPreview {      }      private static void saveStorage(int index) { -        try { -            NbtIo.write(storages[index].toNbt(), saveDir.resolve(index + ".nbt")); +        Path storageFile = saveDir.resolve(index + ".json"); +        try (BufferedWriter writer = Files.newBufferedWriter(storageFile)) { +            SkyblockerMod.GSON.toJson(Storage.CODEC.encodeStart(JsonOps.INSTANCE, storages[index]).result().orElseThrow(), writer);              storages[index].markClean();          } catch (Exception e) { -            LOGGER.error("Failed to save backpack preview file: " + index + ".nbt", e); +            LOGGER.error("Failed to save backpack preview file: " + index + ".json", e);          }      } @@ -129,7 +138,7 @@ public class BackpackPreview {          context.drawTexture(TEXTURE, x, y + rows * 18 + 17, 0, 215, 176, 7);          TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; -        context.drawText(textRenderer, storages[index].name, x + 8, y + 6, 0x404040, false); +        context.drawText(textRenderer, storages[index].name(), x + 8, y + 6, 0x404040, false);          matrices.translate(0f, 0f, 200f);          for (int i = 9; i < storages[index].size(); ++i) { @@ -159,6 +168,10 @@ public class BackpackPreview {      }      static class Storage { +        private static final Codec<Storage> CODEC = RecordCodecBuilder.create(instance -> instance.group( +                Codec.STRING.fieldOf("name").forGetter(Storage::name), +                ItemStack.CODEC.listOf().fieldOf("items").forGetter(Storage::getItemList)) +                .apply(instance, (name, items) -> Storage.create(name, items)));          private final Inventory inventory;          private final String name;          private boolean dirty; @@ -168,6 +181,10 @@ public class BackpackPreview {              this.name = name;              this.dirty = dirty;          } +         +        private String name() { +            return name; +        }          private int size() {              return inventory.size(); @@ -181,23 +198,19 @@ public class BackpackPreview {              dirty = false;          } -        @NotNull -        private static Storage fromNbt(NbtCompound root) { -            SimpleInventory inventory = new SimpleInventory(root.getList("list", NbtCompound.COMPOUND_TYPE).stream().map(NbtCompound.class::cast).map(ItemStack::fromNbt).toArray(ItemStack[]::new)); -            return new Storage(inventory, root.getString("name"), false); +        private static Storage create(String name, List<ItemStack> items) { +            SimpleInventory inventory = new SimpleInventory(items.toArray(ItemStack[]::new)); +            return new Storage(inventory, name, false);          } -        @NotNull -        private NbtCompound toNbt() { -            NbtCompound root = new NbtCompound(); -            NbtList list = new NbtList(); +        private List<ItemStack> getItemList() { +            List<ItemStack> items = new ArrayList<>(); +              for (int i = 0; i < size(); ++i) { -                list.add(getStack(i).writeNbt(new NbtCompound())); +                items.add(getStack(i));              } -            root.put("list", list); -            root.put("size", NbtInt.of(size())); -            root.putString("name", name); -            return root; + +            return items;          }      }  } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java index 562c244c..855a4760 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java @@ -13,13 +13,14 @@ import de.hysky.skyblocker.utils.scheduler.Scheduler;  import it.unimi.dsi.fastutil.Pair;  import net.minecraft.client.MinecraftClient;  import net.minecraft.client.item.TooltipContext; -import net.minecraft.item.DyeableItem; +import net.minecraft.component.type.DyedColorComponent;  import net.minecraft.item.ItemStack;  import net.minecraft.nbt.NbtCompound;  import net.minecraft.nbt.NbtElement;  import net.minecraft.text.MutableText;  import net.minecraft.text.Text;  import net.minecraft.util.Formatting; +  import org.jetbrains.annotations.NotNull;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory; @@ -198,12 +199,13 @@ public class ItemTooltip {              }          } -        if (TooltipInfoType.COLOR.isTooltipEnabledAndHasOrNullWarning(internalID) && stack.getNbt() != null) { +        if (TooltipInfoType.COLOR.isTooltipEnabledAndHasOrNullWarning(internalID)) {              String uuid = ItemUtils.getItemUuid(stack);              boolean hasCustomDye = SkyblockerConfigManager.get().general.customDyeColors.containsKey(uuid) || SkyblockerConfigManager.get().general.customAnimatedDyes.containsKey(uuid); +            int dyeColor = DyedColorComponent.getColor(stack, -1); -            if (!hasCustomDye && DyeableItem.hasColor(stack)) { -                String colorHex = String.format("%06X", DyeableItem.getColor(stack)); +            if (!hasCustomDye && dyeColor != -1) { +                String colorHex = String.format("%06X", dyeColor);                  String expectedHex = ExoticTooltip.getExpectedHex(internalID);                  boolean correctLine = false; @@ -212,13 +214,13 @@ public class ItemTooltip {                      if (existingTooltip.startsWith("Color: ")) {                          correctLine = true; -                        addExoticTooltip(lines, internalID, stack.getNbt(), colorHex, expectedHex, existingTooltip); +                        addExoticTooltip(lines, internalID, ItemUtils.getCustomData(stack).copyNbt(), colorHex, expectedHex, existingTooltip);                          break;                      }                  }                  if (!correctLine) { -                    addExoticTooltip(lines, internalID, stack.getNbt(), colorHex, expectedHex, ""); +                    addExoticTooltip(lines, internalID, ItemUtils.getCustomData(stack).copyNbt(), colorHex, expectedHex, "");                  }              }          } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java index c1eea23a..11c3687e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java @@ -5,15 +5,25 @@ import de.hysky.skyblocker.utils.NEURepoManager;  import io.github.moulberry.repo.constants.PetNumbers;  import io.github.moulberry.repo.data.NEUItem;  import io.github.moulberry.repo.data.Rarity; -import net.minecraft.item.FireworkRocketItem; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.AttributeModifiersComponent; +import net.minecraft.component.type.DyedColorComponent; +import net.minecraft.component.type.FireworkExplosionComponent; +import net.minecraft.component.type.LoreComponent; +import net.minecraft.component.type.NbtComponent; +import net.minecraft.component.type.ProfileComponent;  import net.minecraft.item.ItemStack;  import net.minecraft.nbt.*; +import net.minecraft.registry.Registries;  import net.minecraft.text.Text; +import net.minecraft.util.Identifier;  import net.minecraft.util.Pair;  import java.util.*;  import java.util.regex.Matcher;  import java.util.regex.Pattern; +import java.util.stream.Collectors;  public class ItemStackBuilder {      private static Map<String, Map<Rarity, PetNumbers>> petNums; @@ -31,73 +41,62 @@ public class ItemStackBuilder {          List<Pair<String, String>> injectors = new ArrayList<>(petData(internalName)); -        NbtCompound root = new NbtCompound(); -        root.put("Count", NbtByte.of((byte) 1)); +        String legacyId = item.getMinecraftItemId(); +        Identifier itemId = new Identifier(ItemFixerUpper.convertItemId(legacyId, item.getDamage())); +         +        ItemStack stack = new ItemStack(Registries.ITEM.get(itemId)); -        String id = item.getMinecraftItemId(); -        int damage = item.getDamage(); -        root.put("id", NbtString.of(ItemFixerUpper.convertItemId(id, damage))); +        // Create & Attach ExtraAttributes tag +        NbtCompound customData = new NbtCompound(); +        NbtCompound extraAttributes = new NbtCompound(); +        customData.put(ItemUtils.EXTRA_ATTRIBUTES, extraAttributes); -        NbtCompound tag = new NbtCompound(); -        root.put("tag", tag); - -        NbtCompound extra = new NbtCompound(); -        tag.put(ItemUtils.EXTRA_ATTRIBUTES, extra); -        extra.put(ItemUtils.ID, NbtString.of(internalName)); - -        NbtCompound display = new NbtCompound(); -        tag.put("display", display); +        // Add Skyblock Item Id +        extraAttributes.put(ItemUtils.ID, NbtString.of(internalName)); +        // Item Name          String name = injectData(item.getDisplayName(), injectors); -        display.put("Name", NbtString.of(Text.Serialization.toJsonString(Text.of(name)))); +        stack.set(DataComponentTypes.CUSTOM_NAME, Text.of(name)); -        NbtList lore = new NbtList(); -        display.put("Lore", lore); -        item.getLore().forEach(el -> lore.add(NbtString.of(Text.Serialization.toJsonString(Text.of(injectData(el, injectors)))))); +        // Lore +        stack.set(DataComponentTypes.LORE, new LoreComponent(item.getLore().stream().map(line -> Text.of(injectData(line, injectors))).collect(Collectors.toCollection(() -> new ArrayList<>()))));          String nbttag = item.getNbttag();          // add skull texture          Matcher skullUuid = Pattern.compile("(?<=SkullOwner:\\{)Id:\"(.{36})\"").matcher(nbttag);          Matcher skullTexture = Pattern.compile("(?<=Properties:\\{textures:\\[0:\\{Value:)\"(.+?)\"").matcher(nbttag);          if (skullUuid.find() && skullTexture.find()) { -            NbtCompound skullOwner = new NbtCompound(); -            tag.put("SkullOwner", skullOwner);              UUID uuid = UUID.fromString(skullUuid.group(1)); -            skullOwner.put("Id", NbtHelper.fromUuid(uuid)); -            skullOwner.put("Name", NbtString.of(internalName)); - -            NbtCompound properties = new NbtCompound(); -            skullOwner.put("Properties", properties); -            NbtList textures = new NbtList(); -            properties.put("textures", textures); -            NbtCompound texture = new NbtCompound(); -            textures.add(texture); -            texture.put("Value", NbtString.of(skullTexture.group(1))); +            String textureValue = skullTexture.group(1); + +            stack.set(DataComponentTypes.PROFILE, new ProfileComponent(internalName, Optional.of(uuid), ItemUtils.propertyMapWithTexture(textureValue)));          } +          // add leather armor dye color          Matcher colorMatcher = Pattern.compile("color:(\\d+)").matcher(nbttag);          if (colorMatcher.find()) { -            NbtInt color = NbtInt.of(Integer.parseInt(colorMatcher.group(1))); -            display.put("color", color); +            int color = Integer.parseInt(colorMatcher.group(1)); +            stack.set(DataComponentTypes.DYED_COLOR, new DyedColorComponent(color, false));          }          // add enchantment glint          if (nbttag.contains("ench:")) { -            NbtList enchantments = new NbtList(); -            enchantments.add(new NbtCompound()); -            tag.put("Enchantments", enchantments); +            stack.set(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true);          } +         +        //Hide weapon damage and other useless info +        stack.set(DataComponentTypes.ATTRIBUTE_MODIFIERS, new AttributeModifiersComponent(List.of(), false));          // Add firework star color          Matcher explosionColorMatcher = Pattern.compile("\\{Explosion:\\{(?:Type:[0-9a-z]+,)?Colors:\\[(?<color>[0-9]+)]\\}").matcher(nbttag);          if (explosionColorMatcher.find()) { -            NbtCompound explosion = new NbtCompound(); - -            explosion.putInt("Type", FireworkRocketItem.Type.SMALL_BALL.getId()); //Forget about the actual ball type because it probably doesn't matter -            explosion.putIntArray("Colors", new int[]{Integer.parseInt(explosionColorMatcher.group("color"))}); -            tag.put("Explosion", explosion); +            //Forget about the actual ball type because it probably doesn't matter +            stack.set(DataComponentTypes.FIREWORK_EXPLOSION, new FireworkExplosionComponent(FireworkExplosionComponent.Type.SMALL_BALL, new IntArrayList(Integer.parseInt(explosionColorMatcher.group("color"))), new IntArrayList(), false, false));          } +         +        // Attach custom nbt data +        stack.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(customData)); -        return ItemStack.fromNbt(root); +        return stack;      }      private static List<Pair<String, String>> petData(String internalName) { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java index 44e336d9..961a2cc2 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java @@ -8,10 +8,13 @@ import net.minecraft.client.gui.DrawContext;  import net.minecraft.client.gui.Drawable;  import net.minecraft.client.gui.screen.ButtonTextures;  import net.minecraft.client.gui.widget.ToggleButtonWidget; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.LoreComponent;  import net.minecraft.item.ItemStack;  import net.minecraft.text.Text;  import net.minecraft.util.Formatting;  import net.minecraft.util.Identifier; +  import org.jetbrains.annotations.Nullable;  import java.util.ArrayList; @@ -74,11 +77,8 @@ public class SearchResultsWidget implements Drawable {              this.searchResults.clear();              for (ItemStack entry : ItemRepository.getItems()) {                  String name = entry.getName().toString().toLowerCase(Locale.ENGLISH); -                if (entry.getNbt() == null) { -                    continue; -                } -                String disp = entry.getNbt().getCompound("display").toString().toLowerCase(Locale.ENGLISH); -                if (name.contains(this.searchText) || disp.contains(this.searchText)) +                LoreComponent lore = entry.getOrDefault(DataComponentTypes.LORE, LoreComponent.DEFAULT); +                if (name.contains(this.searchText) || lore.lines().stream().map(Text::getString).anyMatch(s -> s.contains(this.searchText)))                      this.searchResults.add(entry);              }              this.currentPage = 0; 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 51a3d409..8e203b84 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java @@ -5,12 +5,12 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;  import de.hysky.skyblocker.config.SkyblockerConfig;  import de.hysky.skyblocker.config.SkyblockerConfigManager;  import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer;  import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;  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.item.ItemStack;  import net.minecraft.nbt.StringNbtReader;  import net.minecraft.text.Text;  import net.minecraft.util.Formatting; @@ -21,8 +21,6 @@ import java.util.Locale;  import java.util.regex.PatternSyntaxException;  public class QuickNav { -    private static final String skyblockHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}"; -    private static final String dungeonHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}}";      public static void init() {          ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { @@ -74,7 +72,6 @@ public class QuickNav {          return new QuickNavButton(id,                  uiTitleMatches,                  buttonInfo.clickEvent, -                ItemStack.fromNbt(StringNbtReader.parse(nbtString)) -        ); +                ItemStackComponentizationFixer.fixUpItem(StringNbtReader.parse(nbtString)));      }  } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/special/SpecialEffects.java b/src/main/java/de/hysky/skyblocker/skyblock/special/SpecialEffects.java index bc4f98c2..8e10086f 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/special/SpecialEffects.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/special/SpecialEffects.java @@ -1,42 +1,24 @@  package de.hysky.skyblocker.skyblock.special; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +  import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;  import de.hysky.skyblocker.utils.Utils;  import de.hysky.skyblocker.utils.render.RenderHelper;  import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;  import net.minecraft.client.MinecraftClient; -import net.minecraft.enchantment.Enchantments;  import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.nbt.StringNbtReader;  import net.minecraft.particle.ParticleTypes;  import net.minecraft.text.Text; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.regex.Matcher; -import java.util.regex.Pattern;  public class SpecialEffects {  	private static final Logger LOGGER = LoggerFactory.getLogger(SpecialEffects.class);  	private static final Pattern DROP_PATTERN = Pattern.compile("(?:\\[[A-Z+]+] )?(?<player>[A-Za-z0-9_]+) unlocked (?<item>.+)!"); -	private static final ItemStack NECRON_HANDLE = new ItemStack(Items.STICK); -	private static final ItemStack SCROLL = new ItemStack(Items.WRITABLE_BOOK); -	private static ItemStack TIER_5_SKULL; -	private static ItemStack FIFTH_STAR; - -	static { -		NECRON_HANDLE.addEnchantment(Enchantments.PROTECTION, 1); -		SCROLL.addEnchantment(Enchantments.PROTECTION, 1); -		try { -			TIER_5_SKULL = ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-1613868903,-527154034,-1445577520,748807544],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTEwZjlmMTA4NWQ0MDcxNDFlYjc3NjE3YTRhYmRhYWEwOGQ4YWYzM2I5NjAyMDBmZThjMTI2YzFkMTQ0NTY4MiJ9fX0=\"}]}}}}")); -			FIFTH_STAR = ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1904417095,756174249,-1302927470,1407004198],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzFjODA0MjUyN2Y4MWM4ZTI5M2UyODEwMTEzNDg5ZjQzOTRjYzZlZmUxNWQxYWZhYzQzMTU3MWM3M2I2MmRjNCJ9fX0=\"}]}}}}")); -		} catch (Exception e) { -			TIER_5_SKULL = ItemStack.EMPTY; -			FIFTH_STAR = ItemStack.EMPTY; -			LOGGER.error("[Skyblocker Special Effects] Failed to parse NBT for a player head!", e); -		} -	}  	public static void init() {  		ClientReceiveMessageEvents.GAME.register(SpecialEffects::displayRareDropEffect); @@ -45,7 +27,7 @@ public class SpecialEffects {  	private static void displayRareDropEffect(Text message, boolean overlay) {  		//We don't check if we're in dungeons because that check doesn't work in m7 which defeats the point of this  		//It might also allow it to work with Croesus -		if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.specialEffects.rareDungeonDropEffects) { +		if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.specialEffects.rareDungeonDropEffects && !overlay) {  			try {  				String stringForm = message.getString();  				Matcher matcher = DROP_PATTERN.matcher(stringForm); @@ -57,7 +39,7 @@ public class SpecialEffects {  					if (player.equals(client.getSession().getUsername())) {  						ItemStack stack = getStackFromName(matcher.group("item")); -						if (!stack.isEmpty()) { +						if (stack != null && !stack.isEmpty()) {  							RenderHelper.runOnRenderThread(() -> {  								client.particleManager.addEmitter(client.player, ParticleTypes.PORTAL, 30);  								client.gameRenderer.showFloatingItem(stack); @@ -72,20 +54,22 @@ public class SpecialEffects {  	}  	private static ItemStack getStackFromName(String itemName) { -		return switch (itemName) { +		String itemId = switch (itemName) {  			//M7 -			case "Necron Dye" -> new ItemStack(Items.ORANGE_DYE); -			case "Dark Claymore" -> new ItemStack(Items.STONE_SWORD); -			case "Necron's Handle", "Shiny Necron's Handle" -> NECRON_HANDLE; -			case "Enchanted Book (Thunderlord VII)" -> new ItemStack(Items.ENCHANTED_BOOK); -			case "Master Skull - Tier 5" -> TIER_5_SKULL; -			case "Shadow Warp", "Wither Shield", "Implosion" -> SCROLL; -			case "Fifth Master Star" -> FIFTH_STAR; +			case "Necron Dye" -> "NECRON_DYE"; +			case "Dark Claymore" -> "DARK_CLAYMORE"; +			case "Necron's Handle", "Shiny Necron's Handle" -> "NECRON_HANDLE"; +			case "Enchanted Book (Thunderlord VII)" -> "ENCHANTED_BOOK"; +			case "Master Skull - Tier 5" -> "MASTER_SKULL_TIER_5"; +			case "Shadow Warp", "Wither Shield", "Implosion" -> "IMPLOSION_SCROLL"; +			case "Fifth Master Star" -> "FIFTH_MASTER_STAR";  			//M6 -			case "Giant's Sword" -> new ItemStack(Items.IRON_SWORD); +			case "Giant's Sword" -> "GIANTS_SWORD"; -			default -> ItemStack.EMPTY; +			default -> "NONE";  		}; +		 +		return ItemRepository.getItemStack(itemId);  	}  } diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java index 70a8c241..9768ff11 100644 --- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java @@ -1,18 +1,26 @@  package de.hysky.skyblocker.utils; +import com.google.gson.Gson; +import com.google.gson.JsonParser; +import com.mojang.authlib.properties.PropertyMap;  import com.mojang.brigadier.Command;  import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import de.hysky.skyblocker.mixin.accessor.ItemStackAccessor; +import com.mojang.serialization.JsonOps; +  import it.unimi.dsi.fastutil.ints.IntIntPair;  import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.LoreComponent; +import net.minecraft.component.type.NbtComponent; +import net.minecraft.component.type.ProfileComponent;  import net.minecraft.item.ItemStack; +import net.minecraft.item.Items;  import net.minecraft.nbt.NbtCompound;  import net.minecraft.nbt.NbtElement; -import net.minecraft.nbt.StringNbtReader;  import net.minecraft.text.Text; -import net.minecraft.text.Texts;  import net.minecraft.util.Formatting; +import net.minecraft.util.dynamic.Codecs; +  import org.jetbrains.annotations.NotNull;  import org.jetbrains.annotations.Nullable;  import org.slf4j.Logger; @@ -39,13 +47,18 @@ public class ItemUtils {      private static final SimpleDateFormat OLD_OBTAINED_DATE_FORMAT = new SimpleDateFormat("MM/dd/yy");      public static final Pattern NOT_DURABILITY = Pattern.compile("[^0-9 /]");      public static final Predicate<String> FUEL_PREDICATE = line -> line.contains("Fuel: "); +    private static final Gson GSON = new Gson(); //GSON Instance with no config -    public static LiteralArgumentBuilder<FabricClientCommandSource> dumpHeldItemNbtCommand() { -        return literal("dumpHeldItemNbt").executes(context -> { -            context.getSource().sendFeedback(Text.literal("[Skyblocker Debug] Held Item Nbt: " + context.getSource().getPlayer().getMainHandStack().writeNbt(new NbtCompound()))); +    public static LiteralArgumentBuilder<FabricClientCommandSource> dumpHeldItemCommand() { +        return literal("dumpHeldItem").executes(context -> { +            context.getSource().sendFeedback(Text.literal("[Skyblocker Debug] Held Item: " + GSON.toJson(ItemStack.CODEC.encodeStart(JsonOps.INSTANCE, context.getSource().getPlayer().getMainHandStack()).result().orElseThrow())));              return Command.SINGLE_SUCCESS;          });      } +     +    public static NbtComponent getCustomData(@NotNull ItemStack stack) { +        return stack.getOrDefault(DataComponentTypes.CUSTOM_DATA, NbtComponent.DEFAULT); +    }      /**       * Gets the {@code ExtraAttributes} NBT tag from the item stack. @@ -54,7 +67,7 @@ public class ItemUtils {       * @return an optional containing the {@code ExtraAttributes} NBT tag of the item stack       */      public static Optional<NbtCompound> getExtraAttributesOptional(@NotNull ItemStack stack) { -        return Optional.ofNullable(stack.getSubNbt(EXTRA_ATTRIBUTES)); +        return Optional.ofNullable(getExtraAttributes(stack));      }      /** @@ -65,7 +78,9 @@ public class ItemUtils {       */      @Nullable      public static NbtCompound getExtraAttributes(@NotNull ItemStack stack) { -        return stack.getSubNbt(EXTRA_ATTRIBUTES); +    	NbtComponent customData = getCustomData(stack); +        NbtCompound customNbt = customData.copyNbt(); +        return customNbt.contains(EXTRA_ATTRIBUTES) ? customNbt.getCompound(EXTRA_ATTRIBUTES) : null;      }      /** @@ -166,7 +181,7 @@ public class ItemUtils {              return IntIntPair.of(pickonimbusDurability, 5000);          } -        String drillFuel = Formatting.strip(getNbtTooltip(stack, FUEL_PREDICATE)); +        String drillFuel = Formatting.strip(getLoreLineIf(stack, FUEL_PREDICATE));          if (drillFuel != null) {              String[] drillFuelStrings = NOT_DURABILITY.matcher(drillFuel).replaceAll("").trim().split("/");              return IntIntPair.of(Integer.parseInt(drillFuelStrings[0]), Integer.parseInt(drillFuelStrings[1]) * 1000); @@ -176,8 +191,8 @@ public class ItemUtils {      }      @Nullable -    public static String getNbtTooltip(ItemStack item, Predicate<String> predicate) { -        for (Text line : getNbtTooltips(item)) { +    public static String getLoreLineIf(ItemStack item, Predicate<String> predicate) { +        for (Text line : getLore(item)) {              String string = line.getString();              if (predicate.test(string)) {                  return string; @@ -188,8 +203,8 @@ public class ItemUtils {      }      @Nullable -    public static Matcher getNbtTooltip(ItemStack item, Pattern pattern) { -        for (Text line : getNbtTooltips(item)) { +    public static Matcher getLoreLineIfMatch(ItemStack item, Pattern pattern) { +        for (Text line : getLore(item)) {              String string = line.getString();              Matcher matcher = pattern.matcher(string);              if (matcher.matches()) { @@ -200,19 +215,24 @@ public class ItemUtils {          return null;      } -    public static List<Text> getNbtTooltips(ItemStack item) { -        NbtCompound displayNbt = item.getSubNbt("display"); -        if (displayNbt == null || !displayNbt.contains("Lore", NbtElement.LIST_TYPE)) { -            return Collections.emptyList(); -        } +    public static List<Text> getLore(ItemStack item) { +        LoreComponent lore = item.getOrDefault(DataComponentTypes.LORE, LoreComponent.DEFAULT); -        return displayNbt.getList("Lore", NbtElement.STRING_TYPE).stream().map(NbtElement::asString).map(Text.Serialization::fromJson).filter(Objects::nonNull).map(text -> Texts.setStyleIfAbsent(text, ItemStackAccessor.getLORE_STYLE())).map(Text.class::cast).toList(); +        return lore.styledLines(); +    } +     +    public static PropertyMap propertyMapWithTexture(String textureValue) { +    	return Codecs.GAME_PROFILE_PROPERTY_MAP.parse(JsonOps.INSTANCE, JsonParser.parseString("[{\"name\":\"textures\",\"value\":\"" + textureValue + "\"}]")).result().orElseThrow();      }      public static ItemStack getSkyblockerStack() {          try { -            return ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}")); -        } catch (CommandSyntaxException e) { +            ItemStack stack = new ItemStack(Items.PLAYER_HEAD); +            stack.set(DataComponentTypes.PROFILE, new ProfileComponent("SkyblockerStack", Optional.of(java.util.UUID.randomUUID()), propertyMapWithTexture("e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0="))); + +            return stack; +            //return ItemStack.parseOptional(MinecraftClient.getInstance().player.getRegistryManager(), StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}")); +        } catch (Exception e) {              throw new RuntimeException(e);          }      } diff --git a/src/main/java/de/hysky/skyblocker/utils/datafixer/ItemStackComponentizationFixer.java b/src/main/java/de/hysky/skyblocker/utils/datafixer/ItemStackComponentizationFixer.java new file mode 100644 index 00000000..f1306ad5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/datafixer/ItemStackComponentizationFixer.java @@ -0,0 +1,26 @@ +package de.hysky.skyblocker.utils.datafixer; + +import com.mojang.serialization.Dynamic; + +import net.minecraft.datafixer.Schemas; +import net.minecraft.datafixer.TypeReferences; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtOps; + +/** + * Contains a data fixer to convert legacy item NBT to the new components system. + *  + * @see {@link net.minecraft.datafixer.fix.ItemStackComponentizationFix} + */ +public class ItemStackComponentizationFixer { +	private static final int ITEM_NBT_DATA_VERSION = 3817; +	private static final int ITEM_COMPONENTS_DATA_VERSION = 3818; + +	public static ItemStack fixUpItem(NbtCompound nbt) { +		Dynamic<NbtElement> dynamic = Schemas.getFixer().update(TypeReferences.ITEM_STACK, new Dynamic<NbtElement>(NbtOps.INSTANCE, nbt), ITEM_NBT_DATA_VERSION, ITEM_COMPONENTS_DATA_VERSION); + +		return ItemStack.CODEC.parse(dynamic).result().orElseThrow(); +	} +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 451cd684..e5ed5db4 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -34,9 +34,9 @@    "accessWidener": "skyblocker.accesswidener",    "depends": {      "fabricloader": ">=0.15.7", -    "fabric-api": ">=0.96.2+1.20.5", -    "yet_another_config_lib_v3": ">=3.3.2+1.20.4", -    "minecraft": "~1.20.5-alpha.24.7.a" +    "fabric-api": ">=0.96.5+1.20.5", +    "yet_another_config_lib_v3": ">=3.3.3+1.20.4+update.1.20.5-SNAPSHOT+update.1.20.5-SNAPSHOT", +    "minecraft": "~1.20.5-alpha.24.9.a"    },    "conflicts": {      "immediatelyfast": "<=1.2.11+1.20.4" diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index 07168300..8a2205d4 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -5,14 +5,14 @@    "compatibilityLevel": "JAVA_17",    "client": [      "AbstractInventoryScreenMixin", -    "ArmorTrimMixin",      "BatEntityMixin",      "ClientPlayerEntityMixin",      "ClientPlayNetworkHandlerMixin",      "CommandTreeS2CPacketMixin", +    "ComponentHolderMixin",      "DataTrackerMixin",      "DrawContextMixin", -    "DyeableItemMixin", +    "DyedColorComponentMixin",      "EntityRenderDispatcherMixin",      "FarmlandBlockMixin",      "GenericContainerScreenHandlerMixin", @@ -43,7 +43,6 @@      "accessor.EndermanEntityAccessor",      "accessor.FrustumInvoker",      "accessor.HandledScreenAccessor", -    "accessor.ItemStackAccessor",      "accessor.LayeredDrawerAccessor",      "accessor.MessageHandlerAccessor",      "accessor.PlayerListHudAccessor", diff --git a/src/test/java/de/hysky/skyblocker/utils/ItemUtilsTest.java b/src/test/java/de/hysky/skyblocker/utils/ItemUtilsTest.java index 71ff29ef..e5025d7a 100644 --- a/src/test/java/de/hysky/skyblocker/utils/ItemUtilsTest.java +++ b/src/test/java/de/hysky/skyblocker/utils/ItemUtilsTest.java @@ -1,20 +1,23 @@  package de.hysky.skyblocker.utils;  import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer;  import it.unimi.dsi.fastutil.ints.IntIntPair;  import net.minecraft.Bootstrap;  import net.minecraft.SharedConstants;  import net.minecraft.item.ItemStack;  import net.minecraft.nbt.StringNbtReader; +  import org.junit.jupiter.api.Assertions;  import org.junit.jupiter.api.BeforeAll;  import org.junit.jupiter.api.Test;  public class ItemUtilsTest { -    private final ItemStack JUJU_SHORTBOW_OLD = ItemStack.fromNbt(StringNbtReader.parse("{Count:1b,id:\"minecraft:bow\",tag:{Damage:0,Enchantments:[{id:\"minecraft:protection\",lvl:0s}],ExtraAttributes:{art_of_war_count:1,dungeon_item_level:5,enchantments:{aiming:5,chance:4,cubism:5,impaling:3,infinite_quiver:10,overload:5,piercing:1,power:6,snipe:3,telekinesis:1,ultimate_soul_eater:5},hot_potato_count:15,id:\"JUJU_SHORTBOW\",modifier:\"spiritual\",originTag:\"QUICK_CRAFTING\",rarity_upgrades:1,runes:{DRAGON:3},stats_book:54778,timestamp:\"6/5/21 4:41 AM\",uuid:\"b06b8fe2-470a-43f3-b844-658472f20996\"},HideFlags:255,Unbreakable:1b,display:{Lore:['{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gear Score: \"},{\"color\":\"light_purple\",\"text\":\"724 \"},{\"color\":\"dark_gray\",\"text\":\"(2297)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Damage: \"},{\"color\":\"red\",\"text\":\"+371 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"dark_gray\",\"text\":\"(+1,275)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Strength: \"},{\"color\":\"red\",\"text\":\"+107 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"gold\",\"text\":\"[+5] \"},{\"color\":\"blue\",\"text\":\"(+28) \"},{\"color\":\"dark_gray\",\"text\":\"(+386.25)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Chance: \"},{\"color\":\"red\",\"text\":\"+28% \"},{\"color\":\"blue\",\"text\":\"(+12%) \"},{\"color\":\"dark_gray\",\"text\":\"(+40.5%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Damage: \"},{\"color\":\"red\",\"text\":\"+181% \"},{\"color\":\"blue\",\"text\":\"(+55%) \"},{\"color\":\"dark_gray\",\"text\":\"(+637.5%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Shot Cooldown: \"},{\"color\":\"green\",\"text\":\"0.5s\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"color\":\"light_purple\",\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"Soul Eater V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Chance IV\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Cubism V\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Dragon Tracer V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Impaling III\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Infinite Quiver X\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Overload V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Piercing I\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Power VI\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Snipe III\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_purple\",\"text\":\"◆ End Rune III\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Hits \"},{\"color\":\"red\",\"text\":\"3 \"},{\"color\":\"gray\",\"text\":\"mobs on impact.\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Can damage endermen.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"white\",\"text\":\"Kills: \"},{\"color\":\"gold\",\"text\":\"54,778\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Spiritual Bonus\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants a \"},{\"color\":\"green\",\"text\":\"10% \"},{\"color\":\"gray\",\"text\":\"chance to spawn\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"a Spirit Decoy when you kill an\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"enemy in a dungeon.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Shortbow: Instantly shoots!\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"dark_red\",\"text\":\"☠ \"},{\"color\":\"red\",\"text\":\"Requires \"},{\"color\":\"dark_purple\",\"text\":\"Enderman Slayer\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_purple\",\"text\":\"5\"},{\"color\":\"red\",\"text\":\".\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"obfuscated\":true,\"color\":\"gold\",\"text\":\"a\"},{\"text\":\"\"},{\"bold\":false,\"italic\":false,\"underlined\":false,\"obfuscated\":false,\"strikethrough\":false,\"extra\":[{\"text\":\" \"}],\"text\":\"\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"LEGENDARY DUNGEON BOW \"},{\"bold\":true,\"obfuscated\":true,\"color\":\"gold\",\"text\":\"a\"}],\"text\":\"\"}'],Name:'{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Spiritual Juju Shortbow \"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"}],\"text\":\"\"}'}}}")); -    private final ItemStack JUJU_SHORTBOW = ItemStack.fromNbt(StringNbtReader.parse("{Count:1b,id:\"minecraft:bow\",tag:{Damage:0,Enchantments:[{id:\"minecraft:protection\",lvl:0s}],ExtraAttributes:{art_of_war_count:1,dungeon_item_level:5,enchantments:{aiming:5,chance:4,cubism:5,impaling:3,infinite_quiver:10,overload:5,piercing:1,power:6,snipe:3,telekinesis:1,ultimate_soul_eater:5},hot_potato_count:15,id:\"JUJU_SHORTBOW\",modifier:\"spiritual\",originTag:\"QUICK_CRAFTING\",rarity_upgrades:1,runes:{DRAGON:3},stats_book:54778,timestamp:1622882460000L,uuid:\"b06b8fe2-470a-43f3-b844-658472f20996\"},HideFlags:255,Unbreakable:1b,display:{Lore:['{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gear Score: \"},{\"color\":\"light_purple\",\"text\":\"724 \"},{\"color\":\"dark_gray\",\"text\":\"(2297)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Damage: \"},{\"color\":\"red\",\"text\":\"+371 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"dark_gray\",\"text\":\"(+1,275)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Strength: \"},{\"color\":\"red\",\"text\":\"+107 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"gold\",\"text\":\"[+5] \"},{\"color\":\"blue\",\"text\":\"(+28) \"},{\"color\":\"dark_gray\",\"text\":\"(+386.25)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Chance: \"},{\"color\":\"red\",\"text\":\"+28% \"},{\"color\":\"blue\",\"text\":\"(+12%) \"},{\"color\":\"dark_gray\",\"text\":\"(+40.5%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Damage: \"},{\"color\":\"red\",\"text\":\"+181% \"},{\"color\":\"blue\",\"text\":\"(+55%) \"},{\"color\":\"dark_gray\",\"text\":\"(+637.5%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Shot Cooldown: \"},{\"color\":\"green\",\"text\":\"0.5s\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"color\":\"light_purple\",\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"Soul Eater V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Chance IV\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Cubism V\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Dragon Tracer V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Impaling III\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Infinite Quiver X\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Overload V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Piercing I\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Power VI\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Snipe III\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_purple\",\"text\":\"◆ End Rune III\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Hits \"},{\"color\":\"red\",\"text\":\"3 \"},{\"color\":\"gray\",\"text\":\"mobs on impact.\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Can damage endermen.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Shortbow: Instantly shoots!\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"white\",\"text\":\"Kills: \"},{\"color\":\"gold\",\"text\":\"54,778\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Spiritual Bonus\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants a \"},{\"color\":\"green\",\"text\":\"10% \"},{\"color\":\"gray\",\"text\":\"chance to spawn a\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Spirit Decoy when you kill an enemy\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"in a dungeon.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"dark_red\",\"text\":\"☠ \"},{\"color\":\"red\",\"text\":\"Requires \"},{\"color\":\"dark_purple\",\"text\":\"Enderman Slayer 5\"},{\"color\":\"red\",\"text\":\".\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"obfuscated\":true,\"color\":\"gold\",\"text\":\"a\"},{\"text\":\"\"},{\"bold\":false,\"italic\":false,\"underlined\":false,\"obfuscated\":false,\"strikethrough\":false,\"extra\":[{\"text\":\" \"}],\"text\":\"\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"LEGENDARY DUNGEON BOW \"},{\"bold\":true,\"obfuscated\":true,\"color\":\"gold\",\"text\":\"a\"}],\"text\":\"\"}'],Name:'{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Spiritual Juju Shortbow \"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"}],\"text\":\"\"}'}}}")); -    private final ItemStack LIVID_DAGGER = ItemStack.fromNbt(StringNbtReader.parse("{Count:1b,id:\"minecraft:iron_sword\",tag:{Damage:0,Enchantments:[{id:\"minecraft:protection\",lvl:0s}],ExtraAttributes:{dungeon_item_level:5,enchantments:{telekinesis:1,ultimate_one_for_all:1},gems:{JASPER_0:\"FLAWLESS\"},hot_potato_count:15,id:\"LIVID_DAGGER\",modifier:\"fabled\",originTag:\"UNKNOWN\",rarity_upgrades:1,timestamp:\"2/20/21 3:09 PM\",uuid:\"214333db-d71c-4080-8487-00b4666bb9a4\"},HideFlags:255,Unbreakable:1b,display:{Lore:['{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gear Score: \"},{\"color\":\"light_purple\",\"text\":\"933 \"},{\"color\":\"dark_gray\",\"text\":\"(2389)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Damage: \"},{\"color\":\"red\",\"text\":\"+261 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"dark_gray\",\"text\":\"(+900)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Strength: \"},{\"color\":\"red\",\"text\":\"+183 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"blue\",\"text\":\"(+75) \"},{\"color\":\"light_purple\",\"text\":\"(+12) \"},{\"color\":\"dark_gray\",\"text\":\"(+663.75)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Chance: \"},{\"color\":\"red\",\"text\":\"+100% \"},{\"color\":\"dark_gray\",\"text\":\"(+150%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Damage: \"},{\"color\":\"red\",\"text\":\"+105% \"},{\"color\":\"blue\",\"text\":\"(+50%) \"},{\"color\":\"dark_gray\",\"text\":\"(+375%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Bonus Attack Speed: \"},{\"color\":\"red\",\"text\":\"+55% \"},{\"color\":\"dark_gray\",\"text\":\"(+75%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"text\":\" \"},{\"color\":\"dark_purple\",\"text\":\"[\"},{\"color\":\"light_purple\",\"text\":\"❁\"},{\"color\":\"dark_purple\",\"text\":\"]\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"color\":\"light_purple\",\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"One For All I\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Ability: Throw  \"},{\"bold\":true,\"color\":\"yellow\",\"text\":\"RIGHT CLICK\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Throw your dagger at your\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"enemies!\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"Mana Cost: \"},{\"color\":\"dark_aqua\",\"text\":\"150\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"Cooldown: \"},{\"color\":\"green\",\"text\":\"5s\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Your Critical Hits deal \"},{\"color\":\"blue\",\"text\":\"100%\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"more damage if you are behind\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"your target.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Fabled Bonus\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Critical hits have a chance to\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"deal up to \"},{\"color\":\"green\",\"text\":\"15% \"},{\"color\":\"gray\",\"text\":\"extra damage.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"obfuscated\":true,\"color\":\"light_purple\",\"text\":\"a\"},{\"text\":\"\"},{\"bold\":false,\"italic\":false,\"underlined\":false,\"obfuscated\":false,\"strikethrough\":false,\"extra\":[{\"text\":\" \"}],\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"MYTHIC DUNGEON SWORD \"},{\"bold\":true,\"obfuscated\":true,\"color\":\"light_purple\",\"text\":\"a\"}],\"text\":\"\"}'],Name:'{\"italic\":false,\"extra\":[{\"color\":\"light_purple\",\"text\":\"Fabled Livid Dagger \"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"}],\"text\":\"\"}'}}}")); -    private final ItemStack MITHRIL_DRILL_1 = ItemStack.fromNbt(StringNbtReader.parse("{Count:1b,id:\"minecraft:prismarine_shard\",tag:{Damage:21,Enchantments:[{id:\"minecraft:protection\",lvl:0s}],ExtraAttributes:{compact_blocks:97683,drill_fuel:2979,enchantments:{compact:7,efficiency:5,experience:3,fortune:3,telekinesis:1},id:\"MITHRIL_DRILL_1\",modifier:\"fleet\",originTag:\"UNKNOWN\",timestamp:\"1/30/21 5:52 AM\",uuid:\"575ce2c9-251e-4435-9020-de1a2e24b1d0\"},HideFlags:255,display:{Lore:['{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"Breaking Power 5\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Damage: \"},{\"color\":\"red\",\"text\":\"+65\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Mining Speed: \"},{\"color\":\"green\",\"text\":\"+585 \"},{\"color\":\"blue\",\"text\":\"(+25)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Mining Fortune: \"},{\"color\":\"green\",\"text\":\"+30\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Mining Wisdom: \"},{\"color\":\"green\",\"text\":\"+7\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Compact VII \"},{\"color\":\"dark_gray\",\"text\":\"97,683\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gain \"},{\"color\":\"dark_aqua\",\"text\":\"+7☯ Mining Wisdom \"},{\"color\":\"gray\",\"text\":\"and a \"},{\"color\":\"green\",\"text\":\"0.4%\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"green\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"chance to drop an enchanted item.\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"150k blocks to tier up!\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Efficiency V\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants \"},{\"color\":\"green\",\"text\":\"+110 \"},{\"color\":\"gold\",\"text\":\"⸕ Mining Speed\"},{\"color\":\"gray\",\"text\":\".\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Experience III\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants a \"},{\"color\":\"green\",\"text\":\"37.5% \"},{\"color\":\"gray\",\"text\":\"chance for mobs and\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"ores to drop double experience.\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Fortune III\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants \"},{\"color\":\"gold\",\"text\":\"+30☘ Mining Fortune\"},{\"color\":\"gray\",\"text\":\", which\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"increases your chance for multiple\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"drops.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gain \"},{\"color\":\"green\",\"text\":\"+15% \"},{\"color\":\"dark_green\",\"text\":\"Mithril Powder\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"when using this Drill!\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Fuel Tank: \"},{\"color\":\"red\",\"text\":\"Not Installed\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"Increases fuel capacity with part\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"installed.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Drill Engine: \"},{\"color\":\"red\",\"text\":\"Not Installed\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"Increases \"},{\"color\":\"gold\",\"text\":\"⸕ Mining Speed \"},{\"color\":\"gray\",\"text\":\"with part\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"installed.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Upgrade Module: \"},{\"color\":\"red\",\"text\":\"Not Installed\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"Applies a passive upgrade with part\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"installed.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Apply Drill Parts to this Drill by\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"talking to a \"},{\"color\":\"dark_green\",\"text\":\"Drill Mechanic\"},{\"color\":\"gray\",\"text\":\"!\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Fuel: \"},{\"color\":\"dark_green\",\"text\":\"2,979\"},{\"color\":\"dark_gray\",\"text\":\"/3k\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Ability: Mining Speed Boost  \"},{\"bold\":true,\"color\":\"yellow\",\"text\":\"RIGHT CLICK\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants \"},{\"color\":\"green\",\"text\":\"+\"},{\"color\":\"green\",\"text\":\"200% \"},{\"color\":\"gold\",\"text\":\"⸕ Mining Speed \"},{\"color\":\"gray\",\"text\":\"for\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"green\",\"text\":\"15s\"},{\"color\":\"gray\",\"text\":\".\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"Cooldown: \"},{\"color\":\"green\",\"text\":\"120s\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"color\":\"blue\",\"text\":\"RARE DRILL\"}],\"text\":\"\"}'],Name:'{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Fleet Mithril Drill SX-R226\"}],\"text\":\"\"}'}}}")); +    private final ItemStack JUJU_SHORTBOW_OLD = ItemStackComponentizationFixer.fixUpItem(StringNbtReader.parse("{Count:1b,id:\"minecraft:bow\",tag:{Damage:0,Enchantments:[{id:\"minecraft:protection\",lvl:0s}],ExtraAttributes:{art_of_war_count:1,dungeon_item_level:5,enchantments:{aiming:5,chance:4,cubism:5,impaling:3,infinite_quiver:10,overload:5,piercing:1,power:6,snipe:3,telekinesis:1,ultimate_soul_eater:5},hot_potato_count:15,id:\"JUJU_SHORTBOW\",modifier:\"spiritual\",originTag:\"QUICK_CRAFTING\",rarity_upgrades:1,runes:{DRAGON:3},stats_book:54778,timestamp:\"6/5/21 4:41 AM\",uuid:\"b06b8fe2-470a-43f3-b844-658472f20996\"},HideFlags:255,Unbreakable:1b,display:{Lore:['{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gear Score: \"},{\"color\":\"light_purple\",\"text\":\"724 \"},{\"color\":\"dark_gray\",\"text\":\"(2297)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Damage: \"},{\"color\":\"red\",\"text\":\"+371 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"dark_gray\",\"text\":\"(+1,275)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Strength: \"},{\"color\":\"red\",\"text\":\"+107 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"gold\",\"text\":\"[+5] \"},{\"color\":\"blue\",\"text\":\"(+28) \"},{\"color\":\"dark_gray\",\"text\":\"(+386.25)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Chance: \"},{\"color\":\"red\",\"text\":\"+28% \"},{\"color\":\"blue\",\"text\":\"(+12%) \"},{\"color\":\"dark_gray\",\"text\":\"(+40.5%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Damage: \"},{\"color\":\"red\",\"text\":\"+181% \"},{\"color\":\"blue\",\"text\":\"(+55%) \"},{\"color\":\"dark_gray\",\"text\":\"(+637.5%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Shot Cooldown: \"},{\"color\":\"green\",\"text\":\"0.5s\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"color\":\"light_purple\",\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"Soul Eater V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Chance IV\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Cubism V\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Dragon Tracer V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Impaling III\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Infinite Quiver X\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Overload V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Piercing I\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Power VI\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Snipe III\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_purple\",\"text\":\"◆ End Rune III\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Hits \"},{\"color\":\"red\",\"text\":\"3 \"},{\"color\":\"gray\",\"text\":\"mobs on impact.\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Can damage endermen.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"white\",\"text\":\"Kills: \"},{\"color\":\"gold\",\"text\":\"54,778\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Spiritual Bonus\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants a \"},{\"color\":\"green\",\"text\":\"10% \"},{\"color\":\"gray\",\"text\":\"chance to spawn\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"a Spirit Decoy when you kill an\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"enemy in a dungeon.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Shortbow: Instantly shoots!\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"dark_red\",\"text\":\"☠ \"},{\"color\":\"red\",\"text\":\"Requires \"},{\"color\":\"dark_purple\",\"text\":\"Enderman Slayer\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_purple\",\"text\":\"5\"},{\"color\":\"red\",\"text\":\".\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"obfuscated\":true,\"color\":\"gold\",\"text\":\"a\"},{\"text\":\"\"},{\"bold\":false,\"italic\":false,\"underlined\":false,\"obfuscated\":false,\"strikethrough\":false,\"extra\":[{\"text\":\" \"}],\"text\":\"\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"LEGENDARY DUNGEON BOW \"},{\"bold\":true,\"obfuscated\":true,\"color\":\"gold\",\"text\":\"a\"}],\"text\":\"\"}'],Name:'{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Spiritual Juju Shortbow \"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"}],\"text\":\"\"}'}}}")); +    private final ItemStack JUJU_SHORTBOW = ItemStackComponentizationFixer.fixUpItem(StringNbtReader.parse("{Count:1b,id:\"minecraft:bow\",tag:{Damage:0,Enchantments:[{id:\"minecraft:protection\",lvl:0s}],ExtraAttributes:{art_of_war_count:1,dungeon_item_level:5,enchantments:{aiming:5,chance:4,cubism:5,impaling:3,infinite_quiver:10,overload:5,piercing:1,power:6,snipe:3,telekinesis:1,ultimate_soul_eater:5},hot_potato_count:15,id:\"JUJU_SHORTBOW\",modifier:\"spiritual\",originTag:\"QUICK_CRAFTING\",rarity_upgrades:1,runes:{DRAGON:3},stats_book:54778,timestamp:1622882460000L,uuid:\"b06b8fe2-470a-43f3-b844-658472f20996\"},HideFlags:255,Unbreakable:1b,display:{Lore:['{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gear Score: \"},{\"color\":\"light_purple\",\"text\":\"724 \"},{\"color\":\"dark_gray\",\"text\":\"(2297)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Damage: \"},{\"color\":\"red\",\"text\":\"+371 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"dark_gray\",\"text\":\"(+1,275)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Strength: \"},{\"color\":\"red\",\"text\":\"+107 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"gold\",\"text\":\"[+5] \"},{\"color\":\"blue\",\"text\":\"(+28) \"},{\"color\":\"dark_gray\",\"text\":\"(+386.25)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Chance: \"},{\"color\":\"red\",\"text\":\"+28% \"},{\"color\":\"blue\",\"text\":\"(+12%) \"},{\"color\":\"dark_gray\",\"text\":\"(+40.5%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Damage: \"},{\"color\":\"red\",\"text\":\"+181% \"},{\"color\":\"blue\",\"text\":\"(+55%) \"},{\"color\":\"dark_gray\",\"text\":\"(+637.5%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Shot Cooldown: \"},{\"color\":\"green\",\"text\":\"0.5s\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"color\":\"light_purple\",\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"Soul Eater V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Chance IV\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Cubism V\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Dragon Tracer V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Impaling III\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Infinite Quiver X\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Overload V\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Piercing I\"},{\"color\":\"blue\",\"text\":\", \"},{\"color\":\"blue\",\"text\":\"Power VI\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Snipe III\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_purple\",\"text\":\"◆ End Rune III\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Hits \"},{\"color\":\"red\",\"text\":\"3 \"},{\"color\":\"gray\",\"text\":\"mobs on impact.\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Can damage endermen.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Shortbow: Instantly shoots!\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"white\",\"text\":\"Kills: \"},{\"color\":\"gold\",\"text\":\"54,778\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Spiritual Bonus\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants a \"},{\"color\":\"green\",\"text\":\"10% \"},{\"color\":\"gray\",\"text\":\"chance to spawn a\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Spirit Decoy when you kill an enemy\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"in a dungeon.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"dark_red\",\"text\":\"☠ \"},{\"color\":\"red\",\"text\":\"Requires \"},{\"color\":\"dark_purple\",\"text\":\"Enderman Slayer 5\"},{\"color\":\"red\",\"text\":\".\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"obfuscated\":true,\"color\":\"gold\",\"text\":\"a\"},{\"text\":\"\"},{\"bold\":false,\"italic\":false,\"underlined\":false,\"obfuscated\":false,\"strikethrough\":false,\"extra\":[{\"text\":\" \"}],\"text\":\"\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"LEGENDARY DUNGEON BOW \"},{\"bold\":true,\"obfuscated\":true,\"color\":\"gold\",\"text\":\"a\"}],\"text\":\"\"}'],Name:'{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Spiritual Juju Shortbow \"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"}],\"text\":\"\"}'}}}")); +    private final ItemStack LIVID_DAGGER = ItemStackComponentizationFixer.fixUpItem(StringNbtReader.parse("{Count:1b,id:\"minecraft:iron_sword\",tag:{Damage:0,Enchantments:[{id:\"minecraft:protection\",lvl:0s}],ExtraAttributes:{dungeon_item_level:5,enchantments:{telekinesis:1,ultimate_one_for_all:1},gems:{JASPER_0:\"FLAWLESS\"},hot_potato_count:15,id:\"LIVID_DAGGER\",modifier:\"fabled\",originTag:\"UNKNOWN\",rarity_upgrades:1,timestamp:\"2/20/21 3:09 PM\",uuid:\"214333db-d71c-4080-8487-00b4666bb9a4\"},HideFlags:255,Unbreakable:1b,display:{Lore:['{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gear Score: \"},{\"color\":\"light_purple\",\"text\":\"933 \"},{\"color\":\"dark_gray\",\"text\":\"(2389)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Damage: \"},{\"color\":\"red\",\"text\":\"+261 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"dark_gray\",\"text\":\"(+900)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Strength: \"},{\"color\":\"red\",\"text\":\"+183 \"},{\"color\":\"yellow\",\"text\":\"(+30) \"},{\"color\":\"blue\",\"text\":\"(+75) \"},{\"color\":\"light_purple\",\"text\":\"(+12) \"},{\"color\":\"dark_gray\",\"text\":\"(+663.75)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Chance: \"},{\"color\":\"red\",\"text\":\"+100% \"},{\"color\":\"dark_gray\",\"text\":\"(+150%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Crit Damage: \"},{\"color\":\"red\",\"text\":\"+105% \"},{\"color\":\"blue\",\"text\":\"(+50%) \"},{\"color\":\"dark_gray\",\"text\":\"(+375%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Bonus Attack Speed: \"},{\"color\":\"red\",\"text\":\"+55% \"},{\"color\":\"dark_gray\",\"text\":\"(+75%)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"text\":\" \"},{\"color\":\"dark_purple\",\"text\":\"[\"},{\"color\":\"light_purple\",\"text\":\"❁\"},{\"color\":\"dark_purple\",\"text\":\"]\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"color\":\"light_purple\",\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"One For All I\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Ability: Throw  \"},{\"bold\":true,\"color\":\"yellow\",\"text\":\"RIGHT CLICK\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Throw your dagger at your\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"enemies!\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"Mana Cost: \"},{\"color\":\"dark_aqua\",\"text\":\"150\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"Cooldown: \"},{\"color\":\"green\",\"text\":\"5s\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Your Critical Hits deal \"},{\"color\":\"blue\",\"text\":\"100%\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"more damage if you are behind\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"your target.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Fabled Bonus\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Critical hits have a chance to\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"deal up to \"},{\"color\":\"green\",\"text\":\"15% \"},{\"color\":\"gray\",\"text\":\"extra damage.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"obfuscated\":true,\"color\":\"light_purple\",\"text\":\"a\"},{\"text\":\"\"},{\"bold\":false,\"italic\":false,\"underlined\":false,\"obfuscated\":false,\"strikethrough\":false,\"extra\":[{\"text\":\" \"}],\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"\"},{\"bold\":true,\"color\":\"light_purple\",\"text\":\"MYTHIC DUNGEON SWORD \"},{\"bold\":true,\"obfuscated\":true,\"color\":\"light_purple\",\"text\":\"a\"}],\"text\":\"\"}'],Name:'{\"italic\":false,\"extra\":[{\"color\":\"light_purple\",\"text\":\"Fabled Livid Dagger \"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"},{\"color\":\"gold\",\"text\":\"✪\"}],\"text\":\"\"}'}}}")); +    private final ItemStack MITHRIL_DRILL_1 = ItemStackComponentizationFixer.fixUpItem(StringNbtReader.parse("{Count:1b,id:\"minecraft:prismarine_shard\",tag:{Damage:21,Enchantments:[{id:\"minecraft:protection\",lvl:0s}],ExtraAttributes:{compact_blocks:97683,drill_fuel:2979,enchantments:{compact:7,efficiency:5,experience:3,fortune:3,telekinesis:1},id:\"MITHRIL_DRILL_1\",modifier:\"fleet\",originTag:\"UNKNOWN\",timestamp:\"1/30/21 5:52 AM\",uuid:\"575ce2c9-251e-4435-9020-de1a2e24b1d0\"},HideFlags:255,display:{Lore:['{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"Breaking Power 5\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Damage: \"},{\"color\":\"red\",\"text\":\"+65\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Mining Speed: \"},{\"color\":\"green\",\"text\":\"+585 \"},{\"color\":\"blue\",\"text\":\"(+25)\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Mining Fortune: \"},{\"color\":\"green\",\"text\":\"+30\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Mining Wisdom: \"},{\"color\":\"green\",\"text\":\"+7\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Compact VII \"},{\"color\":\"dark_gray\",\"text\":\"97,683\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gain \"},{\"color\":\"dark_aqua\",\"text\":\"+7☯ Mining Wisdom \"},{\"color\":\"gray\",\"text\":\"and a \"},{\"color\":\"green\",\"text\":\"0.4%\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"green\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"chance to drop an enchanted item.\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"150k blocks to tier up!\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Efficiency V\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants \"},{\"color\":\"green\",\"text\":\"+110 \"},{\"color\":\"gold\",\"text\":\"⸕ Mining Speed\"},{\"color\":\"gray\",\"text\":\".\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Experience III\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants a \"},{\"color\":\"green\",\"text\":\"37.5% \"},{\"color\":\"gray\",\"text\":\"chance for mobs and\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"ores to drop double experience.\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Fortune III\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants \"},{\"color\":\"gold\",\"text\":\"+30☘ Mining Fortune\"},{\"color\":\"gray\",\"text\":\", which\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"increases your chance for multiple\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"drops.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Gain \"},{\"color\":\"green\",\"text\":\"+15% \"},{\"color\":\"dark_green\",\"text\":\"Mithril Powder\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"when using this Drill!\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Fuel Tank: \"},{\"color\":\"red\",\"text\":\"Not Installed\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"Increases fuel capacity with part\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"installed.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Drill Engine: \"},{\"color\":\"red\",\"text\":\"Not Installed\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"Increases \"},{\"color\":\"gold\",\"text\":\"⸕ Mining Speed \"},{\"color\":\"gray\",\"text\":\"with part\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"installed.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Upgrade Module: \"},{\"color\":\"red\",\"text\":\"Not Installed\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"gray\",\"text\":\"Applies a passive upgrade with part\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"installed.\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Apply Drill Parts to this Drill by\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"talking to a \"},{\"color\":\"dark_green\",\"text\":\"Drill Mechanic\"},{\"color\":\"gray\",\"text\":\"!\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Fuel: \"},{\"color\":\"dark_green\",\"text\":\"2,979\"},{\"color\":\"dark_gray\",\"text\":\"/3k\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gold\",\"text\":\"Ability: Mining Speed Boost  \"},{\"bold\":true,\"color\":\"yellow\",\"text\":\"RIGHT CLICK\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"Grants \"},{\"color\":\"green\",\"text\":\"+\"},{\"color\":\"green\",\"text\":\"200% \"},{\"color\":\"gold\",\"text\":\"⸕ Mining Speed \"},{\"color\":\"gray\",\"text\":\"for\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"gray\",\"text\":\"\"},{\"color\":\"green\",\"text\":\"15s\"},{\"color\":\"gray\",\"text\":\".\"}],\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"color\":\"dark_gray\",\"text\":\"Cooldown: \"},{\"color\":\"green\",\"text\":\"120s\"}],\"text\":\"\"}','{\"italic\":false,\"text\":\"\"}','{\"italic\":false,\"extra\":[{\"bold\":true,\"color\":\"blue\",\"text\":\"RARE DRILL\"}],\"text\":\"\"}'],Name:'{\"italic\":false,\"extra\":[{\"color\":\"blue\",\"text\":\"Fleet Mithril Drill SX-R226\"}],\"text\":\"\"}'}}}"));      public ItemUtilsTest() throws CommandSyntaxException {      } @@ -25,6 +28,7 @@ public class ItemUtilsTest {          Bootstrap.initialize();      } +    //TODO fix this      @Test      void testGetExtraAttributes() {          Assertions.assertEquals("{art_of_war_count:1,dungeon_item_level:5,enchantments:{aiming:5,chance:4,cubism:5,impaling:3,infinite_quiver:10,overload:5,piercing:1,power:6,snipe:3,telekinesis:1,ultimate_soul_eater:5},hot_potato_count:15,id:\"JUJU_SHORTBOW\",modifier:\"spiritual\",originTag:\"QUICK_CRAFTING\",rarity_upgrades:1,runes:{DRAGON:3},stats_book:54778,timestamp:1622882460000L,uuid:\"b06b8fe2-470a-43f3-b844-658472f20996\"}", ItemUtils.getExtraAttributes(JUJU_SHORTBOW).toString()); diff --git a/src/test/java/de/hysky/skyblocker/utils/datafixer/ItemStackComponentizationFixerTest.java b/src/test/java/de/hysky/skyblocker/utils/datafixer/ItemStackComponentizationFixerTest.java new file mode 100644 index 00000000..11951c44 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/utils/datafixer/ItemStackComponentizationFixerTest.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.utils.datafixer; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.mojang.serialization.JsonOps; + +import net.minecraft.Bootstrap; +import net.minecraft.SharedConstants; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.StringNbtReader; + +public class ItemStackComponentizationFixerTest { +	private final NbtCompound NBT = convertToNbt("{id:\"minecraft:diamond_sword\",Count:1,tag:{ExtraAttributes:{id:\"TEST\"}}}"); +	private final Gson GSON = new Gson(); + +	@BeforeAll +	public static void setup() { +		SharedConstants.createGameVersion(); +		Bootstrap.initialize(); +	} + +	@Test +	void testNbtConversion() { +		Assertions.assertNotEquals(NBT, new NbtCompound()); +	} + +	@Test +	void testDataFixer() { +		ItemStack fixedStack = ItemStackComponentizationFixer.fixUpItem(NBT); +		JsonElement stackJson = ItemStack.CODEC.encodeStart(JsonOps.INSTANCE, fixedStack).result().orElseThrow(); + +		Assertions.assertEquals("{\"id\":\"minecraft:diamond_sword\",\"components\":{\"minecraft:custom_data\":{\"ExtraAttributes\":{\"id\":\"TEST\"}}}}", GSON.toJson(stackJson)); +	} + +	private static NbtCompound convertToNbt(String nbt) { +		try { +			return StringNbtReader.parse(nbt); +		} catch (Exception e) { +			return new NbtCompound(); +		} +	} +} | 
