From 7e0151569477df63601be50c82177ecfd21e5deb Mon Sep 17 00:00:00 2001
From: Linnea Gräf <nea@nea.moe>
Date: Fri, 26 Apr 2024 18:04:45 +0200
Subject: Bump to 1.20.5

---
 .../nea/firmament/mixins/ArmorTexturePatch.java    | 68 ---------------------
 .../moe/nea/firmament/mixins/ChatPeekingPatch.java |  6 +-
 .../firmament/mixins/CustomSkullTexturePatch.java  |  4 +-
 .../moe/nea/firmament/mixins/HudRenderEvents.java  |  5 +-
 .../mixins/LenientProfileComponentPatch.java       | 26 ++++++++
 .../mixins/WorldRenderLastEventPatch.java          |  8 ++-
 .../TestForFirmamentOverridePredicatesPatch.java   |  4 +-
 .../mixins/devenv/DisableCommonPacketWarnings.java |  3 +-
 .../firmament/mixins/devenv/IdentifyCloser.java    | 21 +++++++
 .../mixins/devenv/IdentifyStopperPatch.java        | 21 +++++++
 src/main/kotlin/moe/nea/firmament/Firmament.kt     |  5 +-
 .../moe/nea/firmament/events/ItemTooltipEvent.kt   |  6 +-
 .../moe/nea/firmament/events/TooltipEvent.kt       |  5 +-
 .../nea/firmament/events/WorldRenderLastEvent.kt   |  2 +-
 .../nea/firmament/features/debug/PowerUserTools.kt | 14 +++--
 .../features/inventory/ItemRarityCosmetics.kt      |  8 +--
 .../firmament/features/inventory/SlotLocking.kt    |  4 +-
 .../inventory/storageoverlay/VirtualInventory.kt   |  6 +-
 .../firmament/features/mining/PickaxeAbility.kt    | 14 ++---
 .../features/texturepack/CustomSkyBlockTextures.kt | 40 ++++---------
 .../features/texturepack/DisplayNamePredicate.kt   |  9 ++-
 .../features/texturepack/LorePredicate.kt          | 10 +---
 .../features/texturepack/StringMatcher.kt          |  3 +-
 .../kotlin/moe/nea/firmament/gui/WTitledItem.kt    |  7 ++-
 .../moe/nea/firmament/gui/entity/EntityRenderer.kt |  4 +-
 .../moe/nea/firmament/gui/entity/FakeWorld.kt      | 70 +++++++++++-----------
 .../nea/firmament/gui/entity/ModifyEquipment.kt    |  7 ++-
 .../gui/profileviewer/ProfileViewerLibrary.kt      | 14 +++--
 .../nea/firmament/gui/profileviewer/SkillPage.kt   |  2 +
 .../moe/nea/firmament/rei/NEUItemEntryRenderer.kt  | 41 ++++++++-----
 .../kotlin/moe/nea/firmament/repo/ItemCache.kt     | 61 ++++++-------------
 .../moe/nea/firmament/repo/RepoModResourcePack.kt  | 11 +++-
 src/main/kotlin/moe/nea/firmament/util/ItemUtil.kt | 29 +++------
 src/main/kotlin/moe/nea/firmament/util/MC.kt       | 13 ++--
 .../kotlin/moe/nea/firmament/util/SkyblockId.kt    |  5 +-
 .../util/data/ProfileSpecificDataHolder.kt         |  2 +-
 .../moe/nea/firmament/util/item/NbtItemData.kt     | 31 ++++------
 .../moe/nea/firmament/util/item/SkullItemData.kt   | 20 ++-----
 .../firmament/util/render/RenderInWorldContext.kt  |  7 +--
 39 files changed, 289 insertions(+), 327 deletions(-)
 delete mode 100644 src/main/java/moe/nea/firmament/mixins/ArmorTexturePatch.java
 create mode 100644 src/main/java/moe/nea/firmament/mixins/LenientProfileComponentPatch.java
 create mode 100644 src/main/java/moe/nea/firmament/mixins/devenv/IdentifyCloser.java
 create mode 100644 src/main/java/moe/nea/firmament/mixins/devenv/IdentifyStopperPatch.java

(limited to 'src')

diff --git a/src/main/java/moe/nea/firmament/mixins/ArmorTexturePatch.java b/src/main/java/moe/nea/firmament/mixins/ArmorTexturePatch.java
deleted file mode 100644
index 4e6c1cd..0000000
--- a/src/main/java/moe/nea/firmament/mixins/ArmorTexturePatch.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-package moe.nea.firmament.mixins;
-
-
-import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
-import com.llamalad7.mixinextras.sugar.Local;
-import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
-import net.minecraft.client.render.VertexConsumer;
-import net.minecraft.client.render.VertexConsumerProvider;
-import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
-import net.minecraft.client.render.entity.model.BipedEntityModel;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.entity.EquipmentSlot;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.ArmorItem;
-import net.minecraft.item.ItemStack;
-import net.minecraft.util.Identifier;
-import org.jetbrains.annotations.Nullable;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Unique;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-
-@Mixin(ArmorFeatureRenderer.class)
-public abstract class ArmorTexturePatch<T extends LivingEntity, M extends BipedEntityModel<T>, A extends BipedEntityModel<T>> {
-    @Unique
-    private ItemStack lastRenderedArmorItem;
-
-    @Unique
-    private boolean foundCustomTexture;
-
-    @WrapWithCondition(method = "renderArmorParts", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/model/BipedEntityModel;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;IIFFFF)V"))
-    private boolean preventRenderingLeatherArmorColor(BipedEntityModel instance, MatrixStack matrixStack,
-                                                      VertexConsumer vertexConsumer, int light, int uv,
-                                                      float r, float g, float b, float a,
-                                                      @Local(argsOnly = true) @Nullable String overlay) {
-        if (overlay != null) return true;
-        if (foundCustomTexture) return true;
-        var customOverlayTexture = CustomSkyBlockTextures.INSTANCE.getArmorTexture(this.lastRenderedArmorItem, false, "overlay");
-        return customOverlayTexture == null;
-    }
-
-    @Inject(method = "renderArmor", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItem()Lnet/minecraft/item/Item;"))
-    private void onBeforeRenderArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers,
-                                     T entity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci,
-                                     @Local ItemStack itemStack) {
-        this.lastRenderedArmorItem = itemStack;
-    }
-
-    @Inject(method = "getArmorTexture", at = @At("HEAD"), cancellable = true)
-    private void onGetTexture(ArmorItem item, boolean secondLayer, String overlay, CallbackInfoReturnable<Identifier> cir) {
-        if (this.lastRenderedArmorItem == null) return;
-        var armorTexture = CustomSkyBlockTextures.INSTANCE.getArmorTexture(this.lastRenderedArmorItem, secondLayer, overlay);
-        if (armorTexture != null) {
-            cir.setReturnValue(armorTexture);
-            this.foundCustomTexture = true;
-        } else {
-            this.foundCustomTexture = false;
-        }
-    }
-}
diff --git a/src/main/java/moe/nea/firmament/mixins/ChatPeekingPatch.java b/src/main/java/moe/nea/firmament/mixins/ChatPeekingPatch.java
index 25ba0eb..06d6202 100644
--- a/src/main/java/moe/nea/firmament/mixins/ChatPeekingPatch.java
+++ b/src/main/java/moe/nea/firmament/mixins/ChatPeekingPatch.java
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -11,16 +12,17 @@ import moe.nea.firmament.features.fixes.Fixes;
 import net.minecraft.client.gui.hud.ChatHud;
 import org.spongepowered.asm.mixin.Mixin;
 import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
 
 @Mixin(ChatHud.class)
 public class ChatPeekingPatch {
 
-    @ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z"))
+    @ModifyVariable(method = "render", at = @At(value = "HEAD"), index = 5, argsOnly = true)
     public boolean onGetChatHud(boolean old) {
         return old || Fixes.INSTANCE.shouldPeekChat();
     }
 
-    @ModifyExpressionValue(method = "getHeight", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z"))
+    @ModifyExpressionValue(method = "getHeight()I", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z"))
     public boolean onGetChatHudHeight(boolean old) {
         return old || Fixes.INSTANCE.shouldPeekChat();
     }
diff --git a/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java b/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java
index a8cede4..c43a53c 100644
--- a/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java
+++ b/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -11,6 +12,7 @@ import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
 import net.minecraft.block.SkullBlock;
 import net.minecraft.client.render.RenderLayer;
 import net.minecraft.client.render.block.entity.SkullBlockEntityRenderer;
+import net.minecraft.component.type.ProfileComponent;
 import org.spongepowered.asm.mixin.Mixin;
 import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
@@ -19,7 +21,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 @Mixin(SkullBlockEntityRenderer.class)
 public class CustomSkullTexturePatch {
     @Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true)
-    private static void onGetRenderLayer(SkullBlock.SkullType type, GameProfile profile, CallbackInfoReturnable<RenderLayer> cir) {
+    private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) {
         CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir);
     }
 }
diff --git a/src/main/java/moe/nea/firmament/mixins/HudRenderEvents.java b/src/main/java/moe/nea/firmament/mixins/HudRenderEvents.java
index ff4e995..111ab33 100644
--- a/src/main/java/moe/nea/firmament/mixins/HudRenderEvents.java
+++ b/src/main/java/moe/nea/firmament/mixins/HudRenderEvents.java
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -19,13 +20,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 
 @Mixin(InGameHud.class)
 public class HudRenderEvents {
-    @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getSleepTimer()I"))
+    @Inject(method = "renderSleepOverlay", at = @At(value = "TAIL"))
     public void renderCallBack(DrawContext context, float tickDelta, CallbackInfo ci) {
         HudRenderEvent.Companion.publish(new HudRenderEvent(context, tickDelta));
     }
 
     @Inject(method = "renderHotbarItem", at = @At("HEAD"))
-    public void onRenderHotbarItem(DrawContext context, int x, int y, float tickDelta, PlayerEntity player, ItemStack stack, int seed,CallbackInfo ci) {
+    public void onRenderHotbarItem(DrawContext context, int x, int y, float tickDelta, PlayerEntity player, ItemStack stack, int seed, CallbackInfo ci) {
         if (stack != null && !stack.isEmpty())
             HotbarItemRenderEvent.Companion.publish(new HotbarItemRenderEvent(stack, context, x, y, tickDelta));
     }
diff --git a/src/main/java/moe/nea/firmament/mixins/LenientProfileComponentPatch.java b/src/main/java/moe/nea/firmament/mixins/LenientProfileComponentPatch.java
new file mode 100644
index 0000000..75aeab1
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/mixins/LenientProfileComponentPatch.java
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.mixins;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.mojang.serialization.Codec;
+import net.minecraft.component.type.ProfileComponent;
+import net.minecraft.util.Uuids;
+import org.objectweb.asm.Opcodes;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+import java.util.UUID;
+
+@Mixin(ProfileComponent.class)
+public class LenientProfileComponentPatch {
+    // lambda in RecordCodecBuilder.create for BASE_CODEC
+    @ModifyExpressionValue(method = "method_57508", at = @At(value = "FIELD", opcode = Opcodes.GETSTATIC, target = "Lnet/minecraft/util/Uuids;INT_STREAM_CODEC:Lcom/mojang/serialization/Codec;"))
+    private static Codec<UUID> onStaticInit(Codec<UUID> original) {
+        return Uuids.CODEC;
+    }
+}
diff --git a/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java b/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java
index 268aa01..1ad1ae8 100644
--- a/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java
+++ b/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java
@@ -1,11 +1,13 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
 package moe.nea.firmament.mixins;
 
+import com.llamalad7.mixinextras.sugar.Local;
 import moe.nea.firmament.events.WorldRenderLastEvent;
 import net.minecraft.client.render.*;
 import net.minecraft.client.util.math.MatrixStack;
@@ -24,11 +26,11 @@ public class WorldRenderLastEventPatch {
     private BufferBuilderStorage bufferBuilders;
 
     @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;renderChunkDebugInfo(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;Lnet/minecraft/client/render/Camera;)V", shift = At.Shift.BEFORE))
-    public void onWorldRenderLast(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f positionMatrix, CallbackInfo ci) {
+    public void onWorldRenderLast(float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, Matrix4f matrix4f2, CallbackInfo ci
+        , @Local MatrixStack matrixStack) {
         var event = new WorldRenderLastEvent(
-            matrices, tickDelta, renderBlockOutline,
+            matrixStack, tickDelta, renderBlockOutline,
             camera, gameRenderer, lightmapTextureManager,
-            positionMatrix,
             this.bufferBuilders.getEntityVertexConsumers()
         );
         WorldRenderLastEvent.Companion.publish(event);
diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java
index 4db9fc0..740fbc7 100644
--- a/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java
+++ b/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java
@@ -9,6 +9,7 @@ package moe.nea.firmament.mixins.custommodels;
 import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
 import com.llamalad7.mixinextras.sugar.Local;
 import moe.nea.firmament.features.texturepack.BakedOverrideData;
+import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
 import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
 import moe.nea.firmament.features.texturepack.ModelOverrideData;
 import net.minecraft.client.render.model.json.ModelOverride;
@@ -38,11 +39,12 @@ public class TestForFirmamentOverridePredicatesPatch {
     @ModifyExpressionValue(method = "apply", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z"))
     public boolean testFirmamentOverrides(boolean originalValue,
                                           @Local ModelOverrideList.BakedOverride bakedOverride,
-                                          @Local ItemStack stack) {
+                                          @Local(argsOnly = true) ItemStack stack) {
         if (!originalValue) return false;
         var overrideData = (BakedOverrideData) bakedOverride;
         var overrides = overrideData.getFirmamentOverrides();
         if (overrides == null) return true;
+        if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableModelOverrides()) return false;
         for (FirmamentModelPredicate firmamentOverride : overrides) {
             if (!firmamentOverride.test(stack))
                 return false;
diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/DisableCommonPacketWarnings.java b/src/main/java/moe/nea/firmament/mixins/devenv/DisableCommonPacketWarnings.java
index ef4f2d6..9d8da05 100644
--- a/src/main/java/moe/nea/firmament/mixins/devenv/DisableCommonPacketWarnings.java
+++ b/src/main/java/moe/nea/firmament/mixins/devenv/DisableCommonPacketWarnings.java
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -23,7 +24,7 @@ public class DisableCommonPacketWarnings {
 
     @Inject(method = "warnOnUnknownPayload", at = @At("HEAD"), cancellable = true)
     public void onCustomPacketError(CustomPayload customPayload, CallbackInfo ci) {
-        if (Objects.equals(customPayload.id(), Identifier.of("badlion", "mods"))) {
+        if (Objects.equals(customPayload.getId(), Identifier.of("badlion", "mods"))) {
             ci.cancel();
         }
     }
diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyCloser.java b/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyCloser.java
new file mode 100644
index 0000000..f89eabe
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyCloser.java
@@ -0,0 +1,21 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.mixins.devenv;
+
+import net.minecraft.client.util.Window;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(Window.class)
+public class IdentifyCloser {
+    @Inject(method = "close", at = @At("HEAD"))
+    public void onClose(CallbackInfo ci) {
+        Thread.dumpStack();
+    }
+}
diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyStopperPatch.java b/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyStopperPatch.java
new file mode 100644
index 0000000..a37e4ed
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyStopperPatch.java
@@ -0,0 +1,21 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.mixins.devenv;
+
+import net.minecraft.client.MinecraftClient;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(MinecraftClient.class)
+public class IdentifyStopperPatch {
+    @Inject(method = "scheduleStop", at = @At("HEAD"))
+    private void onStop(CallbackInfo ci) {
+        Thread.dumpStack();
+    }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/Firmament.kt b/src/main/kotlin/moe/nea/firmament/Firmament.kt
index a0549fd..03d4576 100644
--- a/src/main/kotlin/moe/nea/firmament/Firmament.kt
+++ b/src/main/kotlin/moe/nea/firmament/Firmament.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -137,8 +138,8 @@ object Firmament {
             globalJob.cancel()
         })
         registerFirmamentEvents()
-        ItemTooltipCallback.EVENT.register { a, b, c ->
-            ItemTooltipEvent.publish(ItemTooltipEvent(a, b, c))
+        ItemTooltipCallback.EVENT.register { a, b, c, d ->
+            ItemTooltipEvent.publish(ItemTooltipEvent(a, b, c, d))
         }
         ScreenEvents.AFTER_INIT.register(ScreenEvents.AfterInit { client, screen, scaledWidth, scaledHeight ->
             ScreenEvents.afterRender(screen)
diff --git a/src/main/kotlin/moe/nea/firmament/events/ItemTooltipEvent.kt b/src/main/kotlin/moe/nea/firmament/events/ItemTooltipEvent.kt
index ba597cd..94a1678 100644
--- a/src/main/kotlin/moe/nea/firmament/events/ItemTooltipEvent.kt
+++ b/src/main/kotlin/moe/nea/firmament/events/ItemTooltipEvent.kt
@@ -1,17 +1,19 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
 package moe.nea.firmament.events
 
-import net.minecraft.client.item.TooltipContext
+import net.minecraft.client.item.TooltipType
+import net.minecraft.item.Item.TooltipContext
 import net.minecraft.item.ItemStack
 import net.minecraft.text.Text
 
 data class ItemTooltipEvent(
-    val stack: ItemStack, val context: TooltipContext, val lines: MutableList<Text>
+    val stack: ItemStack, val context: TooltipContext, val type: TooltipType, val lines: MutableList<Text>
 ) : FirmamentEvent() {
     companion object : FirmamentEventBus<ItemTooltipEvent>()
 }
diff --git a/src/main/kotlin/moe/nea/firmament/events/TooltipEvent.kt b/src/main/kotlin/moe/nea/firmament/events/TooltipEvent.kt
index 5b82feb..ac827f7 100644
--- a/src/main/kotlin/moe/nea/firmament/events/TooltipEvent.kt
+++ b/src/main/kotlin/moe/nea/firmament/events/TooltipEvent.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -7,14 +8,14 @@
 package moe.nea.firmament.events
 
 import net.minecraft.client.gui.tooltip.Tooltip
-import net.minecraft.client.item.TooltipContext
 import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.item.Item
 import net.minecraft.item.ItemStack
 
 data class TooltipEvent(
     val itemStack: ItemStack,
     val tooltip: Tooltip,
-    val tooltipContext: TooltipContext,
+    val tooltipContext: Item.TooltipContext,
     val player: PlayerEntity?
 ) : FirmamentEvent() {
     companion object : FirmamentEventBus<TooltipEvent>()
diff --git a/src/main/kotlin/moe/nea/firmament/events/WorldRenderLastEvent.kt b/src/main/kotlin/moe/nea/firmament/events/WorldRenderLastEvent.kt
index 025d165..b42e9ab 100644
--- a/src/main/kotlin/moe/nea/firmament/events/WorldRenderLastEvent.kt
+++ b/src/main/kotlin/moe/nea/firmament/events/WorldRenderLastEvent.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -25,7 +26,6 @@ data class WorldRenderLastEvent(
     val camera: Camera,
     val gameRenderer: GameRenderer,
     val lightmapTextureManager: LightmapTextureManager,
-    val positionMatrix: Matrix4f,
     val vertexConsumers: VertexConsumerProvider.Immediate,
 ) : FirmamentEvent() {
     companion object : FirmamentEventBus<WorldRenderLastEvent>()
diff --git a/src/main/kotlin/moe/nea/firmament/features/debug/PowerUserTools.kt b/src/main/kotlin/moe/nea/firmament/features/debug/PowerUserTools.kt
index 9341dad..99ef0d5 100644
--- a/src/main/kotlin/moe/nea/firmament/features/debug/PowerUserTools.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/debug/PowerUserTools.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -8,9 +9,9 @@ package moe.nea.firmament.features.debug
 
 import net.minecraft.block.SkullBlock
 import net.minecraft.block.entity.SkullBlockEntity
+import net.minecraft.component.DataComponentTypes
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
-import net.minecraft.nbt.NbtHelper
 import net.minecraft.text.Text
 import net.minecraft.util.hit.BlockHitResult
 import net.minecraft.util.hit.HitResult
@@ -27,7 +28,6 @@ import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
 import moe.nea.firmament.util.ClipboardUtils
 import moe.nea.firmament.util.MC
 import moe.nea.firmament.util.focusedItemStack
-import moe.nea.firmament.util.getOrCreateCompoundTag
 import moe.nea.firmament.util.skyBlockId
 
 object PowerUserTools : FirmamentFeature {
@@ -120,7 +120,8 @@ object PowerUserTools : FirmamentFeature {
                 lastCopiedStack =
                     Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.modelid", model.toString()))
             } else if (it.matches(TConfig.copyNbtData)) {
-                val nbt = item.orCreateNbt.toString()
+                // TODO: copy full nbt
+                val nbt = item.get(DataComponentTypes.CUSTOM_DATA)?.nbt?.toString() ?: "<empty>"
                 ClipboardUtils.setTextContent(nbt)
                 lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.nbt"))
             } else if (it.matches(TConfig.copySkullTexture)) {
@@ -128,7 +129,7 @@ object PowerUserTools : FirmamentFeature {
                     lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-skull"))
                     return@subscribe
                 }
-                val profile = NbtHelper.toGameProfile(item.orCreateNbt.getOrCreateCompoundTag("SkullOwner"))
+                val profile = item.get(DataComponentTypes.PROFILE)
                 if (profile == null) {
                     lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-profile"))
                     return@subscribe
@@ -140,7 +141,10 @@ object PowerUserTools : FirmamentFeature {
                 }
                 ClipboardUtils.setTextContent(skullTexture.toString())
                 lastCopiedStack =
-                    Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString()))
+                    Pair(
+                        item,
+                        Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString())
+                    )
                 println("Copied skull id: $skullTexture")
             }
         }
diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/ItemRarityCosmetics.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/ItemRarityCosmetics.kt
index f3af27d..ac53546 100644
--- a/src/main/kotlin/moe/nea/firmament/features/inventory/ItemRarityCosmetics.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/inventory/ItemRarityCosmetics.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -9,7 +10,6 @@ package moe.nea.firmament.features.inventory
 import java.awt.Color
 import net.minecraft.client.gui.DrawContext
 import net.minecraft.item.ItemStack
-import net.minecraft.nbt.NbtElement
 import net.minecraft.util.Formatting
 import net.minecraft.util.Identifier
 import moe.nea.firmament.events.HotbarItemRenderEvent
@@ -17,6 +17,8 @@ import moe.nea.firmament.events.SlotRenderEvents
 import moe.nea.firmament.features.FirmamentFeature
 import moe.nea.firmament.gui.config.ManagedConfig
 import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.item.loreAccordingToNbt
+import moe.nea.firmament.util.unformattedString
 
 object ItemRarityCosmetics : FirmamentFeature {
     override val identifier: String
@@ -47,9 +49,7 @@ object ItemRarityCosmetics : FirmamentFeature {
     }
     private val ItemStack.skyblockLoreRarityColor: Triple<Float, Float, Float>?
         get() {
-            val lore =
-                getOrCreateSubNbt(ItemStack.DISPLAY_KEY).getList(ItemStack.LORE_KEY, NbtElement.STRING_TYPE.toInt())
-            val entry = lore.getString(lore.size - 1)
+            val entry = loreAccordingToNbt.lastOrNull()?.unformattedString ?: ""
             return rarityToColor.entries.find { (k, v) -> k in entry }?.value
         }
 
diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt
index 43243f1..9bf2182 100644
--- a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt
@@ -1,8 +1,10 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
+
 @file:UseSerializers(DashlessUUIDSerializer::class)
 
 package moe.nea.firmament.features.inventory
@@ -90,7 +92,7 @@ object SlotLocking : FirmamentFeature {
         if (sellItem == null) return false
         if (sellItem.displayNameAccordingToNbt?.unformattedString == "Sell Item") return true
         val lore = sellItem.loreAccordingToNbt
-        return (lore.lastOrNull() ?: return false).value?.unformattedString == "Click to buyback!"
+        return (lore.lastOrNull() ?: return false).unformattedString == "Click to buyback!"
     }
 
     override fun onLoad() {
diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt
index 102b197..6b467eb 100644
--- a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -22,6 +23,7 @@ import net.minecraft.nbt.NbtList
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
 import net.minecraft.nbt.NbtSizeTracker
+import moe.nea.firmament.util.MC
 
 @Serializable(with = VirtualInventory.Serializer::class)
 data class VirtualInventory(
@@ -44,13 +46,13 @@ data class VirtualInventory(
             val s = decoder.decodeString()
             val n = NbtIo.readCompressed(ByteArrayInputStream(s.decodeBase64Bytes()), NbtSizeTracker.of(100_000_000))
             val items = n.getList(INVENTORY, NbtCompound.COMPOUND_TYPE.toInt())
-            return VirtualInventory(items.map { ItemStack.fromNbt(it as NbtCompound) })
+            return VirtualInventory(items.map { ItemStack.fromNbtOrEmpty(MC.defaultRegistries, it as NbtCompound) })
         }
 
         override fun serialize(encoder: Encoder, value: VirtualInventory) {
             val list = NbtList()
             value.stacks.forEach {
-                list.add(NbtCompound().also(it::writeNbt))
+                list.add(it.encode(MC.defaultRegistries))
             }
             val baos = ByteArrayOutputStream()
             NbtIo.writeCompressed(NbtCompound().also { it.put(INVENTORY, list) }, baos)
diff --git a/src/main/kotlin/moe/nea/firmament/features/mining/PickaxeAbility.kt b/src/main/kotlin/moe/nea/firmament/features/mining/PickaxeAbility.kt
index 24a39e0..956ffc3 100644
--- a/src/main/kotlin/moe/nea/firmament/features/mining/PickaxeAbility.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/mining/PickaxeAbility.kt
@@ -91,11 +91,9 @@ object PickaxeAbility : FirmamentFeature {
         DurabilityBarEvent.subscribe {
             if (!TConfig.drillFuelBar) return@subscribe
             val lore = it.item.loreAccordingToNbt
-            if (lore.lastOrNull()?.value?.unformattedString?.contains("DRILL") != true) return@subscribe
+            if (lore.lastOrNull()?.unformattedString?.contains("DRILL") != true) return@subscribe
             val maxFuel = lore.firstNotNullOfOrNull {
-                fuelPattern.useMatch(
-                    it.value?.unformattedString ?: return@firstNotNullOfOrNull null
-                ) {
+                fuelPattern.useMatch(it.unformattedString) {
                     parseShortNumber(group("maxFuel"))
                 }
             } ?: return@subscribe
@@ -115,7 +113,7 @@ object PickaxeAbility : FirmamentFeature {
             if (MC.screen?.title?.unformattedString == "Heart of the Mountain") {
                 val name = it.stack.displayNameAccordingToNbt?.unformattedString ?: return@subscribe
                 val cooldown = it.stack.loreAccordingToNbt.firstNotNullOfOrNull {
-                    cooldownPattern.useMatch(it.value?.unformattedString ?: return@firstNotNullOfOrNull null) {
+                    cooldownPattern.useMatch(it.unformattedString) {
                         parseTimePattern(group("cooldown"))
                     }
                 } ?: return@subscribe
@@ -134,15 +132,15 @@ object PickaxeAbility : FirmamentFeature {
 
     fun getCooldownFromLore(itemStack: ItemStack): PickaxeAbilityData? {
         val lore = itemStack.loreAccordingToNbt
-        if (!lore.any { it.value?.unformattedString?.contains("Breaking Power") == true })
+        if (!lore.any { it.unformattedString.contains("Breaking Power") == true })
             return null
         val cooldown = lore.firstNotNullOfOrNull {
-            cooldownPattern.useMatch(it.value?.unformattedString ?: return@firstNotNullOfOrNull null) {
+            cooldownPattern.useMatch(it.unformattedString) {
                 parseTimePattern(group("cooldown"))
             }
         } ?: return null
         val name = lore.firstNotNullOfOrNull {
-            abilityPattern.useMatch(it.value?.unformattedString ?: return@firstNotNullOfOrNull null) {
+            abilityPattern.useMatch(it.unformattedString) {
                 group("name")
             }
         } ?: return null
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt
index 3fb5e9c..9c2eafc 100644
--- a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt
@@ -7,21 +7,20 @@
 
 package moe.nea.firmament.features.texturepack
 
-import com.mojang.authlib.GameProfile
 import com.mojang.authlib.minecraft.MinecraftProfileTexture
+import com.mojang.authlib.properties.Property
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
 import net.minecraft.block.SkullBlock
 import net.minecraft.client.MinecraftClient
 import net.minecraft.client.render.RenderLayer
 import net.minecraft.client.util.ModelIdentifier
-import net.minecraft.item.ItemStack
+import net.minecraft.component.type.ProfileComponent
 import net.minecraft.util.Identifier
 import moe.nea.firmament.events.CustomItemModelEvent
 import moe.nea.firmament.events.TickEvent
 import moe.nea.firmament.features.FirmamentFeature
 import moe.nea.firmament.gui.config.ManagedConfig
 import moe.nea.firmament.util.IdentityCharacteristics
-import moe.nea.firmament.util.MC
 import moe.nea.firmament.util.item.decodeProfileTextureProperty
 import moe.nea.firmament.util.skyBlockId
 
@@ -53,12 +52,12 @@ object CustomSkyBlockTextures : FirmamentFeature {
         }
     }
 
-    private val skullTextureCache = mutableMapOf<IdentityCharacteristics<GameProfile>, Any>()
+    private val skullTextureCache = mutableMapOf<IdentityCharacteristics<ProfileComponent>, Any>()
     private val sentinelPresentInvalid = Object()
 
     private val mcUrlRegex = "https?://textures.minecraft.net/texture/([a-fA-F0-9]+)".toRegex()
-    fun getSkullId(profile: GameProfile): String? {
-        val textureProperty = MC.instance.sessionService.getPackedTextures(profile) ?: return null
+
+    fun getSkullId(textureProperty: Property): String? {
         val texture = decodeProfileTextureProperty(textureProperty) ?: return null
         val textureUrl =
             texture.textures[MinecraftProfileTexture.Type.SKIN]?.url ?: return null
@@ -66,40 +65,23 @@ object CustomSkyBlockTextures : FirmamentFeature {
         return mcUrlData.groupValues[1]
     }
 
-    fun getSkullTexture(profile: GameProfile): Identifier? {
-        val id = getSkullId(profile) ?: return null
+    fun getSkullTexture(profile: ProfileComponent): Identifier? {
+        val id = getSkullId(profile.properties["textures"].firstOrNull() ?: return null) ?: return null
         return Identifier("firmskyblock", "textures/placedskull/$id.png")
     }
 
-    fun getArmorTexture(
-        itemStack: ItemStack, secondLayer: Boolean,
-        overlay: String?
-    ): Identifier? {
-        val modelIdentifier = CustomItemModelEvent.getModelIdentifier(itemStack) ?: return null
-        // Vanilla scheme: "textures/models/armor/" + var10000 + "_layer_" + (secondLayer ? 2 : 1) + (overlay == null ? "" : "_" + overlay) + ".png";
-        val overlayPart = if (overlay != null) "_$overlay" else ""
-        val identifier = Identifier(
-            modelIdentifier.namespace,
-            "textures/models/armor/${modelIdentifier.path}_layer_${if (secondLayer) 2 else 1}$overlayPart.png"
-        )
-        if (MC.resourceManager.getResource(identifier).isPresent) {
-            return identifier
-        }
-        return null
-    }
-
     fun modifySkullTexture(
         type: SkullBlock.SkullType?,
-        profile: GameProfile?,
+        component: ProfileComponent?,
         cir: CallbackInfoReturnable<RenderLayer>
     ) {
         if (type != SkullBlock.Type.PLAYER) return
         if (!TConfig.skullsEnabled) return
-        if (profile == null) return
-        val ic = IdentityCharacteristics(profile)
+        if (component == null) return
+        val ic = IdentityCharacteristics(component)
 
         val n = skullTextureCache.getOrPut(ic) {
-            val id = getSkullTexture(profile) ?: return@getOrPut sentinelPresentInvalid
+            val id = getSkullTexture(component) ?: return@getOrPut sentinelPresentInvalid
             if (!MinecraftClient.getInstance().resourceManager.getResource(id).isPresent) {
                 return@getOrPut sentinelPresentInvalid
             }
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt
index 373910a..7a1255b 100644
--- a/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt
@@ -10,14 +10,13 @@ import com.google.gson.JsonElement
 import net.minecraft.item.ItemStack
 import net.minecraft.nbt.NbtElement
 import net.minecraft.nbt.NbtString
+import moe.nea.firmament.util.item.displayNameAccordingToNbt
+import moe.nea.firmament.util.item.loreAccordingToNbt
 
 data class DisplayNamePredicate(val stringMatcher: StringMatcher) : FirmamentModelPredicate {
     override fun test(stack: ItemStack): Boolean {
-        val display = stack.getOrCreateSubNbt(ItemStack.DISPLAY_KEY)
-        return if (display.contains(ItemStack.NAME_KEY, NbtElement.STRING_TYPE.toInt()))
-            stringMatcher.matches(display.get(ItemStack.NAME_KEY) as NbtString)
-        else
-            false
+        val display = stack.displayNameAccordingToNbt
+        return stringMatcher.matches(display)
     }
 
     object Parser : FirmamentModelPredicateParser {
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt
index 604d29c..d10814d 100644
--- a/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt
@@ -8,8 +8,7 @@ package moe.nea.firmament.features.texturepack
 
 import com.google.gson.JsonElement
 import net.minecraft.item.ItemStack
-import net.minecraft.nbt.NbtElement
-import net.minecraft.nbt.NbtString
+import moe.nea.firmament.util.item.loreAccordingToNbt
 
 class LorePredicate(val matcher: StringMatcher) : FirmamentModelPredicate {
     object Parser : FirmamentModelPredicateParser {
@@ -19,10 +18,7 @@ class LorePredicate(val matcher: StringMatcher) : FirmamentModelPredicate {
     }
 
     override fun test(stack: ItemStack): Boolean {
-        val display = stack.getOrCreateSubNbt(ItemStack.DISPLAY_KEY)
-        if (!display.contains(ItemStack.LORE_KEY, NbtElement.LIST_TYPE.toInt()))
-            return false
-        val lore = display.getList(ItemStack.LORE_KEY, NbtElement.STRING_TYPE.toInt())
-        return lore.any { matcher.matches(it as NbtString)}
+        val lore = stack.loreAccordingToNbt
+        return lore.any { matcher.matches(it) }
     }
 }
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt
index e9d39a8..8c1ccbc 100644
--- a/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt
@@ -12,6 +12,7 @@ import com.google.gson.JsonPrimitive
 import java.util.function.Predicate
 import net.minecraft.nbt.NbtString
 import net.minecraft.text.Text
+import moe.nea.firmament.util.MC
 import moe.nea.firmament.util.removeColorCodes
 
 interface StringMatcher {
@@ -27,7 +28,7 @@ interface StringMatcher {
         val isString = stringStart >= 0 && string.subSequence(0, stringStart).isBlank()
         val isJson = jsonStart >= 0 && string.subSequence(0, jsonStart).isBlank()
         if (isString || isJson)
-            return matches(Text.Serialization.fromJson(string) ?: return false)
+            return matches(Text.Serialization.fromJson(string, MC.defaultRegistries) ?: return false)
         return matches(string)
     }
 
diff --git a/src/main/kotlin/moe/nea/firmament/gui/WTitledItem.kt b/src/main/kotlin/moe/nea/firmament/gui/WTitledItem.kt
index 817c099..a1d4d68 100644
--- a/src/main/kotlin/moe/nea/firmament/gui/WTitledItem.kt
+++ b/src/main/kotlin/moe/nea/firmament/gui/WTitledItem.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -11,13 +12,13 @@ import io.github.cottonmc.cotton.gui.client.BackgroundPainter
 import io.github.cottonmc.cotton.gui.widget.TooltipBuilder
 import io.github.cottonmc.cotton.gui.widget.WWidget
 import net.minecraft.client.gui.DrawContext
-import net.minecraft.client.item.TooltipContext
+import net.minecraft.client.item.TooltipType
 import net.minecraft.item.ItemStack
 import net.minecraft.text.Text
 import moe.nea.firmament.util.MC
 
 open class WTitledItem(var stack: ItemStack, val countString: Text = Text.empty()) : WWidget() {
-    var backgroundPainter:BackgroundPainter = BackgroundPainter.SLOT
+    var backgroundPainter: BackgroundPainter = BackgroundPainter.SLOT
     override fun canResize(): Boolean = true
     override fun paint(context: DrawContext, x: Int, y: Int, mouseX: Int, mouseY: Int) {
         backgroundPainter.paintBackground(context, x, y, this)
@@ -32,7 +33,7 @@ open class WTitledItem(var stack: ItemStack, val countString: Text = Text.empty(
     }
 
     override fun addTooltip(tooltip: TooltipBuilder) {
-        tooltip.add(*stack.getTooltip(null, TooltipContext.BASIC).toTypedArray())
+        tooltip.add(*stack.getTooltip(null, null, TooltipType.BASIC).toTypedArray())
     }
 
 }
diff --git a/src/main/kotlin/moe/nea/firmament/gui/entity/EntityRenderer.kt b/src/main/kotlin/moe/nea/firmament/gui/entity/EntityRenderer.kt
index 65b5d7d..44fd538 100644
--- a/src/main/kotlin/moe/nea/firmament/gui/entity/EntityRenderer.kt
+++ b/src/main/kotlin/moe/nea/firmament/gui/entity/EntityRenderer.kt
@@ -136,7 +136,7 @@ object EntityRenderer {
                 posY,
                 posX + 50,
                 posY + 80,
-                (minOf(2F / maxSize, 1F) * 30).toInt(),
+                minOf(2F / maxSize, 1F) * 30,
                 -bottomOffset,
                 mouseX,
                 mouseY,
@@ -155,7 +155,7 @@ object EntityRenderer {
         y1: Int,
         x2: Int,
         y2: Int,
-        size: Int,
+        size: Float,
         bottomOffset: Float,
         mouseX: Float,
         mouseY: Float,
diff --git a/src/main/kotlin/moe/nea/firmament/gui/entity/FakeWorld.kt b/src/main/kotlin/moe/nea/firmament/gui/entity/FakeWorld.kt
index 4cdfc45..ac1b33b 100644
--- a/src/main/kotlin/moe/nea/firmament/gui/entity/FakeWorld.kt
+++ b/src/main/kotlin/moe/nea/firmament/gui/entity/FakeWorld.kt
@@ -16,11 +16,13 @@ import kotlin.jvm.optionals.getOrNull
 import kotlin.streams.asSequence
 import net.minecraft.block.Block
 import net.minecraft.block.BlockState
-import net.minecraft.client.world.ClientWorld
+import net.minecraft.component.type.MapIdComponent
 import net.minecraft.entity.Entity
 import net.minecraft.entity.player.PlayerEntity
 import net.minecraft.fluid.Fluid
 import net.minecraft.item.map.MapState
+import net.minecraft.recipe.BrewingRecipeRegistry
+import net.minecraft.recipe.Ingredient
 import net.minecraft.recipe.RecipeManager
 import net.minecraft.registry.BuiltinRegistries
 import net.minecraft.registry.DynamicRegistryManager
@@ -29,10 +31,10 @@ import net.minecraft.registry.RegistryKey
 import net.minecraft.registry.RegistryKeys
 import net.minecraft.registry.RegistryWrapper
 import net.minecraft.registry.entry.RegistryEntry
+import net.minecraft.registry.entry.RegistryEntryInfo
 import net.minecraft.registry.entry.RegistryEntryList
 import net.minecraft.registry.entry.RegistryEntryOwner
 import net.minecraft.registry.tag.TagKey
-import net.minecraft.resource.featuretoggle.FeatureFlag
 import net.minecraft.resource.featuretoggle.FeatureFlags
 import net.minecraft.resource.featuretoggle.FeatureSet
 import net.minecraft.scoreboard.Scoreboard
@@ -119,6 +121,10 @@ fun <T> makeRegistry(registryWrapper: RegistryWrapper.Impl<T>, key: RegistryKey<
             return key
         }
 
+        override fun getEntryInfo(key: RegistryKey<T>?): Optional<RegistryEntryInfo> {
+            TODO("Not yet implemented")
+        }
+
         override fun getLifecycle(): Lifecycle {
             return Lifecycle.stable()
         }
@@ -194,18 +200,18 @@ fun <T> makeRegistry(registryWrapper: RegistryWrapper.Impl<T>, key: RegistryKey<
             return registryWrapper.getOptional(key ?: return Optional.empty())
         }
 
+        override fun getEntry(id: Identifier?): Optional<RegistryEntry.Reference<T>> {
+            TODO("Not yet implemented")
+        }
+
         override fun createEntry(value: T): RegistryEntry.Reference<T> {
-            TODO()
+            TODO("Not yet implemented")
         }
 
         override fun contains(key: RegistryKey<T>?): Boolean {
             return getEntry(key).isPresent
         }
 
-        override fun getEntryLifecycle(entry: T): Lifecycle {
-            return Lifecycle.stable()
-        }
-
         override fun getId(value: T): Identifier? {
             return (inverseLookup[value] ?: return null).value
         }
@@ -236,7 +242,9 @@ fun createDynamicRegistry(): DynamicRegistryManager.Immutable {
     }
 }
 
-class FakeWorld(registries: DynamicRegistryManager.Immutable = createDynamicRegistry()) : World(
+class FakeWorld(
+    registries: DynamicRegistryManager.Immutable = createDynamicRegistry(),
+) : World(
     Properties,
     RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")),
     registries,
@@ -252,16 +260,8 @@ class FakeWorld(registries: DynamicRegistryManager.Immutable = createDynamicRegi
     0, 0
 ) {
     object Properties : MutableWorldProperties {
-        override fun getSpawnX(): Int {
-            return 0
-        }
-
-        override fun getSpawnY(): Int {
-            return 0
-        }
-
-        override fun getSpawnZ(): Int {
-            return 0
+        override fun getSpawnPos(): BlockPos {
+            return BlockPos.ORIGIN
         }
 
         override fun getSpawnAngle(): Float {
@@ -303,17 +303,7 @@ class FakeWorld(registries: DynamicRegistryManager.Immutable = createDynamicRegi
             return false
         }
 
-        override fun setSpawnX(spawnX: Int) {
-        }
-
-        override fun setSpawnY(spawnY: Int) {
-        }
-
-        override fun setSpawnZ(spawnZ: Int) {
-        }
-
-        override fun setSpawnAngle(spawnAngle: Float) {
-        }
+        override fun setSpawnPos(pos: BlockPos?, angle: Float) {}
     }
 
     override fun getPlayers(): List<PlayerEntity> {
@@ -361,7 +351,11 @@ class FakeWorld(registries: DynamicRegistryManager.Immutable = createDynamicRegi
 
     class FakeChunkManager(val world: FakeWorld) : ChunkManager() {
         override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk {
-            return EmptyChunk(world, ChunkPos(x,z), world.registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS))
+            return EmptyChunk(
+                world,
+                ChunkPos(x, z),
+                world.registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS)
+            )
         }
 
         override fun getWorld(): BlockView {
@@ -406,7 +400,7 @@ class FakeWorld(registries: DynamicRegistryManager.Immutable = createDynamicRegi
     override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) {
     }
 
-    override fun emitGameEvent(event: GameEvent?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) {
+    override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) {
     }
 
     override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) {
@@ -435,15 +429,15 @@ class FakeWorld(registries: DynamicRegistryManager.Immutable = createDynamicRegi
         return TickManager()
     }
 
-    override fun getMapState(id: String?): MapState? {
+    override fun getMapState(id: MapIdComponent?): MapState? {
         return null
     }
 
-    override fun putMapState(id: String?, state: MapState?) {
+    override fun putMapState(id: MapIdComponent?, state: MapState?) {
     }
 
-    override fun getNextMapId(): Int {
-        return 0
+    override fun getNextMapId(): MapIdComponent {
+        return MapIdComponent(0)
     }
 
     override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) {
@@ -454,7 +448,7 @@ class FakeWorld(registries: DynamicRegistryManager.Immutable = createDynamicRegi
     }
 
     override fun getRecipeManager(): RecipeManager {
-        return RecipeManager()
+        return RecipeManager(registryManager)
     }
 
     object FakeEntityLookup : EntityLookup<Entity> {
@@ -488,4 +482,8 @@ class FakeWorld(registries: DynamicRegistryManager.Immutable = createDynamicRegi
     override fun getEntityLookup(): EntityLookup<Entity> {
         return FakeEntityLookup
     }
+
+    override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry {
+        return BrewingRecipeRegistry.EMPTY
+    }
 }
diff --git a/src/main/kotlin/moe/nea/firmament/gui/entity/ModifyEquipment.kt b/src/main/kotlin/moe/nea/firmament/gui/entity/ModifyEquipment.kt
index e438f59..261b8f5 100644
--- a/src/main/kotlin/moe/nea/firmament/gui/entity/ModifyEquipment.kt
+++ b/src/main/kotlin/moe/nea/firmament/gui/entity/ModifyEquipment.kt
@@ -7,9 +7,11 @@
 package moe.nea.firmament.gui.entity
 
 import com.google.gson.JsonObject
+import net.minecraft.component.DataComponentTypes
+import net.minecraft.component.type.DyedColorComponent
 import net.minecraft.entity.EquipmentSlot
 import net.minecraft.entity.LivingEntity
-import net.minecraft.item.DyeableArmorItem
+import net.minecraft.item.ArmorItem
 import net.minecraft.item.Item
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
@@ -51,9 +53,8 @@ object ModifyEquipment : EntityModifier {
     }
 
     private fun coloredLeatherArmor(leatherArmor: Item, data: String): ItemStack {
-        require(leatherArmor is DyeableArmorItem)
         val stack = ItemStack(leatherArmor)
-        leatherArmor.setColor(stack, data.toInt(16))
+        stack.set(DataComponentTypes.DYED_COLOR, DyedColorComponent(data.toInt(16), false))
         return stack
     }
 }
diff --git a/src/main/kotlin/moe/nea/firmament/gui/profileviewer/ProfileViewerLibrary.kt b/src/main/kotlin/moe/nea/firmament/gui/profileviewer/ProfileViewerLibrary.kt
index 4248654..0c99f67 100644
--- a/src/main/kotlin/moe/nea/firmament/gui/profileviewer/ProfileViewerLibrary.kt
+++ b/src/main/kotlin/moe/nea/firmament/gui/profileviewer/ProfileViewerLibrary.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -12,9 +13,6 @@ import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription
 import io.github.cottonmc.cotton.gui.widget.WGridPanel
 import io.github.cottonmc.cotton.gui.widget.WText
 import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment
-import moe.nea.firmament.gui.WTitledItem
-import moe.nea.firmament.util.ScreenUtil
-import moe.nea.firmament.util.modifyLore
 import moe.nea.lisp.LispData
 import moe.nea.lisp.LispExecutionContext
 import moe.nea.lisp.LispParser
@@ -23,16 +21,20 @@ import moe.nea.lisp.bind.LispBinding
 import moe.nea.lisp.bind.UnmapForeignObject
 import net.minecraft.command.argument.ItemStringReader
 import net.minecraft.item.ItemStack
-import net.minecraft.registry.Registries
 import net.minecraft.text.Text
+import moe.nea.firmament.gui.WTitledItem
+import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.ScreenUtil
+import moe.nea.firmament.util.item.setCustomName
+import moe.nea.firmament.util.modifyLore
 
 class ProfileViewerLibrary {
 
     @LispBinding("mk-item")
     fun makeItem(itemType: String, title: String, vararg lore: String): LispData.ForeignObject<ItemStack> {
-        val item = ItemStringReader.item(Registries.ITEM.readOnlyWrapper, StringReader(itemType))
+        val item = ItemStringReader(MC.defaultRegistries).consume(StringReader(itemType))
         val itemStack = ItemStack(item.item.value())
-        itemStack.nbt = item.nbt
+        itemStack.applyComponentsFrom(item.components)
         itemStack.modifyLore { lore.map { Text.literal(it) } }
         itemStack.setCustomName(Text.literal(title))
         return LispData.ForeignObject(itemStack)
diff --git a/src/main/kotlin/moe/nea/firmament/gui/profileviewer/SkillPage.kt b/src/main/kotlin/moe/nea/firmament/gui/profileviewer/SkillPage.kt
index 76c88c2..64f4f2c 100644
--- a/src/main/kotlin/moe/nea/firmament/gui/profileviewer/SkillPage.kt
+++ b/src/main/kotlin/moe/nea/firmament/gui/profileviewer/SkillPage.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -32,6 +33,7 @@ import net.minecraft.text.Style
 import net.minecraft.text.Text
 import net.minecraft.util.DyeColor
 import net.minecraft.util.Formatting
+import moe.nea.firmament.util.item.setCustomName
 
 object SkillPage : ProfilePage {
 
diff --git a/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntryRenderer.kt b/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntryRenderer.kt
index b3e5a10..fb608be 100644
--- a/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntryRenderer.kt
+++ b/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntryRenderer.kt
@@ -1,6 +1,7 @@
 /*
- * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
  * SPDX-FileCopyrightText: 2018-2023 shedaniel <daniel@shedaniel.me>
+ * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  * SPDX-License-Identifier: MIT
@@ -17,16 +18,19 @@ import me.shedaniel.rei.api.client.entry.renderer.EntryRenderer
 import me.shedaniel.rei.api.client.gui.widgets.Tooltip
 import me.shedaniel.rei.api.client.gui.widgets.TooltipContext
 import me.shedaniel.rei.api.common.entry.EntryStack
-import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry
 import net.minecraft.client.MinecraftClient
 import net.minecraft.client.gui.DrawContext
+import net.minecraft.client.item.TooltipType
 import net.minecraft.client.render.DiffuseLighting
+import net.minecraft.client.render.LightmapTextureManager
 import net.minecraft.client.render.OverlayTexture
 import net.minecraft.client.render.VertexConsumerProvider
 import net.minecraft.client.render.model.BakedModel
 import net.minecraft.client.render.model.json.ModelTransformationMode
 import net.minecraft.client.texture.SpriteAtlasTexture
+import net.minecraft.item.Item
 import net.minecraft.item.ItemStack
+import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry
 
 object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<SBItemStack, BakedModel> {
     override fun render(
@@ -43,7 +47,13 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<S
     val minecraft = MinecraftClient.getInstance()
 
     override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? {
-        return entry.asItemEntry().getTooltip(tooltipContext, false)
+        return Tooltip.create(
+            entry.asItemEntry().value.getTooltip(
+                Item.TooltipContext.DEFAULT,
+                null,
+                TooltipType.BASIC
+            )
+        )
     }
 
     override fun getExtraData(entry: EntryStack<SBItemStack>): BakedModel {
@@ -60,11 +70,11 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<S
         graphics: DrawContext,
         delta: Float
     ) {
-        setupGL(model)
         val modelViewStack = RenderSystem.getModelViewStack()
-        modelViewStack.push()
-        modelViewStack.scale(20.0f, -20.0f, 1.0f)
+        modelViewStack.pushMatrix()
+        modelViewStack.scale(20.0f, 20.0f, 1.0f)
         RenderSystem.applyModelViewMatrix()
+        setupGL(model)
     }
 
     fun setupGL(model: BakedModel) {
@@ -93,10 +103,10 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<S
         if (entry.isEmpty) return
         val value = entry.asItemEntry().value
         graphics.matrices.push()
-        graphics.matrices.translate(bounds.centerX.toFloat() / 20.0f, bounds!!.centerY.toFloat() / -20.0f, 0.0f)
+        graphics.matrices.translate(bounds.centerX.toFloat() / 20.0f, bounds.centerY.toFloat() / 20.0f, 0.0f)
         graphics.matrices.scale(
             bounds.getWidth().toFloat() / 20.0f,
-            (bounds.getWidth() + bounds.getHeight()).toFloat() / 2.0f / 20.0f,
+            -(bounds.getWidth() + bounds.getHeight()).toFloat() / 2.0f / 20.0f,
             1.0f
         )
         minecraft
@@ -107,7 +117,7 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<S
                 false,
                 graphics.matrices,
                 immediate,
-                15728880,
+                LightmapTextureManager.MAX_LIGHT_COORDINATE,
                 OverlayTexture.DEFAULT_UV,
                 model
             )
@@ -121,9 +131,9 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<S
         graphics: DrawContext,
         delta: Float
     ) {
-        this.endGL(model)
-        RenderSystem.getModelViewStack().pop()
+        RenderSystem.getModelViewStack().popMatrix()
         RenderSystem.applyModelViewMatrix()
+        this.endGL(model)
     }
 
     fun endGL(model: BakedModel) {
@@ -145,19 +155,20 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<S
         delta: Float
     ) {
         val modelViewStack = RenderSystem.getModelViewStack()
-        modelViewStack.push()
-        modelViewStack.multiplyPositionMatrix(graphics.matrices.peek().positionMatrix)
+        modelViewStack.pushMatrix()
+        modelViewStack.mul(graphics.matrices.peek().positionMatrix)
         modelViewStack.translate(bounds.x.toFloat(), bounds.y.toFloat(), 0.0f)
         modelViewStack.scale(
             bounds.width.toFloat() / 16.0f,
-            (bounds.getWidth() + bounds.getHeight()).toFloat() / 2.0f / 16.0f,
+            -(bounds.getWidth() + bounds.getHeight()).toFloat() / 2.0f / 16.0f,
             1.0f
         )
         RenderSystem.applyModelViewMatrix()
         renderOverlay(DrawContext(minecraft, graphics.vertexConsumers), entry.asItemEntry())
-        modelViewStack.pop()
+        modelViewStack.popMatrix()
         RenderSystem.applyModelViewMatrix()
     }
+
     fun renderOverlay(graphics: DrawContext, entry: EntryStack<ItemStack>) {
         if (!entry.isEmpty) {
             graphics.drawItemInSlot(MinecraftClient.getInstance().textRenderer, entry.value, 0, 0, null)
diff --git a/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt b/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt
index 067069b..adeb1c5 100644
--- a/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt
+++ b/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt
@@ -1,13 +1,12 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
 package moe.nea.firmament.repo
 
-import com.mojang.authlib.GameProfile
-import com.mojang.authlib.minecraft.MinecraftProfileTexture
 import com.mojang.serialization.Dynamic
 import io.github.cottonmc.cotton.gui.client.CottonHud
 import io.github.moulberry.repo.IReloadable
@@ -19,28 +18,27 @@ import java.util.concurrent.ConcurrentHashMap
 import org.apache.logging.log4j.LogManager
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
+import kotlin.jvm.optionals.getOrNull
 import net.minecraft.SharedConstants
-import net.minecraft.block.entity.SkullBlockEntity
 import net.minecraft.client.resource.language.I18n
+import net.minecraft.component.DataComponentTypes
+import net.minecraft.component.type.NbtComponent
 import net.minecraft.datafixer.Schemas
 import net.minecraft.datafixer.TypeReferences
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 import net.minecraft.nbt.NbtCompound
 import net.minecraft.nbt.NbtElement
-import net.minecraft.nbt.NbtHelper
-import net.minecraft.nbt.NbtList
 import net.minecraft.nbt.NbtOps
-import net.minecraft.nbt.NbtString
 import net.minecraft.text.Text
 import moe.nea.firmament.Firmament
 import moe.nea.firmament.util.LegacyTagParser
+import moe.nea.firmament.util.MC
 import moe.nea.firmament.util.SkyblockId
 import moe.nea.firmament.util.appendLore
-import moe.nea.firmament.util.getOrCreateList
-import moe.nea.firmament.util.item.MinecraftProfileTextureKt
-import moe.nea.firmament.util.item.MinecraftTexturesPayloadKt
-import moe.nea.firmament.util.item.setTextures
+import moe.nea.firmament.util.item.setCustomName
+import moe.nea.firmament.util.item.setSkullOwner
+import moe.nea.firmament.util.modifyLore
 import moe.nea.firmament.util.skyblockId
 
 object ItemCache : IReloadable {
@@ -90,10 +88,11 @@ object ItemCache : IReloadable {
             val oldItemTag = get10809CompoundTag()
             val modernItemTag = oldItemTag.transformFrom10809ToModern()
                 ?: return brokenItemStack(this)
-            val itemInstance = ItemStack.fromNbt(modernItemTag)
-            if (itemInstance.nbt?.contains("Enchantments") == true) {
-                itemInstance.enchantments.add(NbtCompound())
-            }
+            val itemInstance =
+                ItemStack.fromNbt(MC.defaultRegistries, modernItemTag).getOrNull() ?: return brokenItemStack(this)
+            val extraAttributes = oldItemTag.getCompound("tag").getCompound("ExtraAttributes")
+            if (extraAttributes != null)
+                itemInstance.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes))
             return itemInstance
         } catch (e: Exception) {
             e.printStackTrace()
@@ -117,19 +116,11 @@ object ItemCache : IReloadable {
     }
 
     fun ItemStack.applyLoreReplacements(loreReplacements: Map<String, String>) {
-        val component = getOrCreateSubNbt("display")
-        val lore = component.getOrCreateList("Lore", NbtString.STRING_TYPE)
-        val newLore = NbtList()
-        lore.forEach {
-            newLore.add(
-                NbtString.of(
-                    Text.Serialization.toJsonString(
-                        Text.Serialization.fromJson(it.asString())!!.applyLoreReplacements(loreReplacements)
-                    )
-                )
-            )
+        modifyLore { lore ->
+            lore.map {
+                it.applyLoreReplacements(loreReplacements)
+            }
         }
-        component["Lore"] = newLore
     }
 
     fun Text.applyLoreReplacements(loreReplacements: Map<String, String>): Text {
@@ -141,10 +132,8 @@ object ItemCache : IReloadable {
         return Text.literal(string).styled { this.style }
     }
 
-
     fun NEUItem.getIdentifier() = skyblockId.identifier
 
-
     var job: Job? = null
 
     override fun reload(repository: NEURepository) {
@@ -189,21 +178,7 @@ object ItemCache : IReloadable {
         }
         val itemStack = ItemStack(Items.PLAYER_HEAD)
         itemStack.setCustomName(Text.literal("§r§6" + NumberFormat.getInstance().format(coinAmount) + " Coins"))
-        val nbt: NbtCompound = itemStack.orCreateNbt
-        nbt[SkullBlockEntity.SKULL_OWNER_KEY] = NbtHelper.writeGameProfile(
-            NbtCompound(),
-            GameProfile(uuid, "CoolGuy123").also {
-                it.setTextures(
-                    MinecraftTexturesPayloadKt(
-                        mapOf(
-                            MinecraftProfileTexture.Type.SKIN to MinecraftProfileTextureKt(texture),
-                        ),
-                        uuid,
-                        "CoolGuy123"
-                    )
-                )
-            }
-        )
+        itemStack.setSkullOwner(uuid, texture)
         return itemStack
     }
 }
diff --git a/src/main/kotlin/moe/nea/firmament/repo/RepoModResourcePack.kt b/src/main/kotlin/moe/nea/firmament/repo/RepoModResourcePack.kt
index 810e2a4..a26fa19 100644
--- a/src/main/kotlin/moe/nea/firmament/repo/RepoModResourcePack.kt
+++ b/src/main/kotlin/moe/nea/firmament/repo/RepoModResourcePack.kt
@@ -22,9 +22,12 @@ import net.minecraft.resource.InputSupplier
 import net.minecraft.resource.NamespaceResourceManager
 import net.minecraft.resource.Resource
 import net.minecraft.resource.ResourcePack
+import net.minecraft.resource.ResourcePackInfo
+import net.minecraft.resource.ResourcePackSource
 import net.minecraft.resource.ResourceType
 import net.minecraft.resource.metadata.ResourceMetadata
 import net.minecraft.resource.metadata.ResourceMetadataReader
+import net.minecraft.text.Text
 import net.minecraft.util.Identifier
 import net.minecraft.util.PathUtil
 import moe.nea.firmament.Firmament
@@ -113,12 +116,16 @@ class RepoModResourcePack(val basePath: Path) : ModResourcePack {
         )
     }
 
-    override fun getName(): String {
-        return "NEU Repo Resources"
+    override fun getInfo(): ResourcePackInfo {
+        return ResourcePackInfo("neurepo", Text.literal("NEU Repo"), ResourcePackSource.BUILTIN, Optional.empty())
     }
 
     override fun getFabricModMetadata(): ModMetadata {
         return FabricLoader.getInstance().getModContainer("firmament")
             .get().metadata
     }
+
+    override fun createOverlay(overlay: String): ModResourcePack {
+        return RepoModResourcePack(basePath.resolve(overlay))
+    }
 }
diff --git a/src/main/kotlin/moe/nea/firmament/util/ItemUtil.kt b/src/main/kotlin/moe/nea/firmament/util/ItemUtil.kt
index 5a7a116..4ae226e 100644
--- a/src/main/kotlin/moe/nea/firmament/util/ItemUtil.kt
+++ b/src/main/kotlin/moe/nea/firmament/util/ItemUtil.kt
@@ -10,33 +10,22 @@ package moe.nea.firmament.util
 import net.minecraft.item.ItemStack
 import net.minecraft.nbt.NbtCompound
 import net.minecraft.nbt.NbtList
-import net.minecraft.nbt.NbtString
 import net.minecraft.text.Text
+import moe.nea.firmament.util.item.loreAccordingToNbt
 
 
 fun ItemStack.appendLore(args: List<Text>) {
     if (args.isEmpty()) return
-    val compoundTag = getOrCreateSubNbt("display")
-    val loreList = compoundTag.getOrCreateList("Lore", NbtString.STRING_TYPE)
-    for (arg in args) {
-        loreList.add(NbtString.of(Text.Serialization.toJsonString(arg)))
+    modifyLore {
+        val loreList = loreAccordingToNbt.toMutableList()
+        for (arg in args) {
+            loreList.add(arg)
+        }
+        loreList
     }
 }
 
 fun ItemStack.modifyLore(update: (List<Text>) -> List<Text>) {
-    val compoundTag = getOrCreateSubNbt("display")
-    val loreList = compoundTag.getOrCreateList("Lore", NbtString.STRING_TYPE)
-    val parsed = loreList.map { Text.Serialization.fromJson(it.asString())!! }
-    val updated = update(parsed)
-    loreList.clear()
-    loreList.addAll(updated.map { NbtString.of(Text.Serialization.toJsonString(it)) })
-}
-
-
-fun NbtCompound.getOrCreateList(label: String, tag: Byte): NbtList = getList(label, tag.toInt()).also {
-    put(label, it)
-}
-
-fun NbtCompound.getOrCreateCompoundTag(label: String): NbtCompound = getCompound(label).also {
-    put(label, it)
+    val loreList = loreAccordingToNbt
+    loreAccordingToNbt = update(loreList)
 }
diff --git a/src/main/kotlin/moe/nea/firmament/util/MC.kt b/src/main/kotlin/moe/nea/firmament/util/MC.kt
index 5c4bf5c..2fb1c75 100644
--- a/src/main/kotlin/moe/nea/firmament/util/MC.kt
+++ b/src/main/kotlin/moe/nea/firmament/util/MC.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -7,13 +8,12 @@
 package moe.nea.firmament.util
 
 import io.github.moulberry.repo.data.Coordinate
-import java.time.Instant
 import java.util.concurrent.ConcurrentLinkedQueue
 import net.minecraft.client.MinecraftClient
 import net.minecraft.client.gui.screen.ingame.HandledScreen
-import net.minecraft.network.message.ArgumentSignatureDataMap
-import net.minecraft.network.message.LastSeenMessagesCollector.LastSeenMessages
 import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
+import net.minecraft.registry.BuiltinRegistries
+import net.minecraft.registry.RegistryWrapper
 import net.minecraft.resource.ReloadableResourceManagerImpl
 import net.minecraft.text.Text
 import net.minecraft.util.math.BlockPos
@@ -40,14 +40,9 @@ object MC {
 
     fun sendServerCommand(command: String) {
         val nh = player?.networkHandler ?: return
-        val lastSeenMessages: LastSeenMessages = nh.lastSeenMessagesCollector.collect()
         nh.sendPacket(
             CommandExecutionC2SPacket(
                 command,
-                Instant.now(),
-                0L,
-                ArgumentSignatureDataMap.EMPTY,
-                lastSeenMessages.update()
             )
         )
     }
@@ -77,6 +72,8 @@ object MC {
         set(value) = MinecraftClient.getInstance().setScreen(value)
     inline val handledScreen: HandledScreen<*>? get() = MinecraftClient.getInstance().currentScreen as? HandledScreen<*>
     inline val window get() = MinecraftClient.getInstance().window
+    inline val currentRegistries: RegistryWrapper.WrapperLookup? get() = world?.registryManager
+    val defaultRegistries: RegistryWrapper.WrapperLookup = BuiltinRegistries.createWrapperLookup()
 }
 
 
diff --git a/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt b/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt
index fb63eba..413231d 100644
--- a/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt
+++ b/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt
@@ -16,6 +16,7 @@ import kotlinx.serialization.Serializable
 import kotlinx.serialization.UseSerializers
 import kotlinx.serialization.decodeFromString
 import kotlinx.serialization.json.Json
+import net.minecraft.component.DataComponentTypes
 import net.minecraft.item.ItemStack
 import net.minecraft.nbt.NbtCompound
 import net.minecraft.util.Identifier
@@ -76,7 +77,7 @@ data class HypixelPetInfo(
 private val jsonparser = Json { ignoreUnknownKeys = true }
 
 val ItemStack.extraAttributes: NbtCompound
-    get() = getOrCreateSubNbt("ExtraAttributes")
+    get() = get(DataComponentTypes.CUSTOM_DATA)?.nbt ?: NbtCompound()
 
 val ItemStack.skyblockUUIDString: String?
     get() = extraAttributes.getString("uuid")?.takeIf { it.isNotBlank() }
@@ -121,7 +122,7 @@ val ItemStack.skyBlockId: SkyblockId?
                 else SkyblockId("${enchantName.uppercase()};${enchantmentData.getInt(enchantName)}")
             }
 
-            // TODO: PARTY_HAT_CRAB{,_ANIMATED,_SLOTH}
+            // TODO: PARTY_HAT_CRAB{,_ANIMATED,_SLOTH},POTION
             else -> {
                 SkyblockId(id)
             }
diff --git a/src/main/kotlin/moe/nea/firmament/util/data/ProfileSpecificDataHolder.kt b/src/main/kotlin/moe/nea/firmament/util/data/ProfileSpecificDataHolder.kt
index a016c32..063076b 100644
--- a/src/main/kotlin/moe/nea/firmament/util/data/ProfileSpecificDataHolder.kt
+++ b/src/main/kotlin/moe/nea/firmament/util/data/ProfileSpecificDataHolder.kt
@@ -1,5 +1,6 @@
 /*
  * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
  *
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
@@ -35,7 +36,6 @@ abstract class ProfileSpecificDataHolder<S>(
 
     init {
         allConfigs = readValues()
-        readValues()
         IDataHolder.putDataHolder(this::class, this)
     }
 
diff --git a/src/main/kotlin/moe/nea/firmament/util/item/NbtItemData.kt b/src/main/kotlin/moe/nea/firmament/util/item/NbtItemData.kt
index 80628ae..c366dc2 100644
--- a/src/main/kotlin/moe/nea/firmament/util/item/NbtItemData.kt
+++ b/src/main/kotlin/moe/nea/firmament/util/item/NbtItemData.kt
@@ -7,27 +7,20 @@
 
 package moe.nea.firmament.util.item
 
+import net.minecraft.component.DataComponentTypes
+import net.minecraft.component.type.LoreComponent
 import net.minecraft.item.ItemStack
-import net.minecraft.nbt.NbtElement
-import net.minecraft.nbt.NbtString
 import net.minecraft.text.Text
 
-fun textFromNbt() {
-
-}
-
-val ItemStack.loreAccordingToNbt
-    get() = getOrCreateSubNbt(ItemStack.DISPLAY_KEY).getList(ItemStack.LORE_KEY, NbtElement.STRING_TYPE.toInt())
-        .map {
-            lazy(LazyThreadSafetyMode.NONE) {
-                Text.Serialization.fromJson((it as NbtString).asString())
-            }
-        }
+var ItemStack.loreAccordingToNbt
+    get() = get(DataComponentTypes.LORE)?.lines ?: listOf()
+    set(value) {
+        set(DataComponentTypes.LORE, LoreComponent(value))
+    }
 
 val ItemStack.displayNameAccordingToNbt
-    get() = getOrCreateSubNbt(ItemStack.DISPLAY_KEY).let {
-        if (it.contains(ItemStack.NAME_KEY, NbtElement.STRING_TYPE.toInt()))
-            Text.Serialization.fromJson(it.getString(ItemStack.NAME_KEY))
-        else
-            null
-    }
+    get() = get(DataComponentTypes.CUSTOM_NAME) ?: get(DataComponentTypes.ITEM_NAME) ?: item.name
+
+fun ItemStack.setCustomName(literal: Text) {
+    set(DataComponentTypes.CUSTOM_NAME, literal)
+}
diff --git a/src/main/kotlin/moe/nea/firmament/util/item/SkullItemData.kt b/src/main/kotlin/moe/nea/firmament/util/item/SkullItemData.kt
index a061ee4..96bf49a 100644
--- a/src/main/kotlin/moe/nea/firmament/util/item/SkullItemData.kt
+++ b/src/main/kotlin/moe/nea/firmament/util/item/SkullItemData.kt
@@ -17,15 +17,12 @@ import kotlinx.datetime.Clock
 import kotlinx.datetime.Instant
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.UseSerializers
-import kotlinx.serialization.decodeFromString
 import kotlinx.serialization.encodeToString
-import net.minecraft.block.entity.SkullBlockEntity
+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.NbtHelper
 import moe.nea.firmament.Firmament
-import moe.nea.firmament.repo.set
 import moe.nea.firmament.util.assertTrueOr
 import moe.nea.firmament.util.json.DashlessUUIDSerializer
 import moe.nea.firmament.util.json.InstantAsLongSerializer
@@ -60,11 +57,7 @@ fun ItemStack.setEncodedSkullOwner(uuid: UUID, encodedData: String) {
     assert(this.item == Items.PLAYER_HEAD)
     val gameProfile = GameProfile(uuid, "LameGuy123")
     gameProfile.properties.put(propertyTextures, Property(propertyTextures, encodedData.padBase64()))
-    val nbt: NbtCompound = this.orCreateNbt
-    nbt[SkullBlockEntity.SKULL_OWNER_KEY] = NbtHelper.writeGameProfile(
-        NbtCompound(),
-        gameProfile
-    )
+    this.set(DataComponentTypes.PROFILE, ProfileComponent(gameProfile))
 }
 
 val zeroUUID = UUID.fromString("d3cb85e2-3075-48a1-b213-a9bfb62360c1")
@@ -76,12 +69,7 @@ fun ItemStack.setSkullOwner(uuid: UUID, url: String) {
             mapOf(MinecraftProfileTexture.Type.SKIN to MinecraftProfileTextureKt(url))
         )
     )
-    val nbt: NbtCompound = this.orCreateNbt
-    nbt[SkullBlockEntity.SKULL_OWNER_KEY] = NbtHelper.writeGameProfile(
-        NbtCompound(),
-        gameProfile
-    )
-
+    this.set(DataComponentTypes.PROFILE, ProfileComponent(gameProfile))
 }
 
 
diff --git a/src/main/kotlin/moe/nea/firmament/util/render/RenderInWorldContext.kt b/src/main/kotlin/moe/nea/firmament/util/render/RenderInWorldContext.kt
index ecaf3f9..d5fb464 100644
--- a/src/main/kotlin/moe/nea/firmament/util/render/RenderInWorldContext.kt
+++ b/src/main/kotlin/moe/nea/firmament/util/render/RenderInWorldContext.kt
@@ -23,7 +23,6 @@ import net.minecraft.client.render.VertexFormat
 import net.minecraft.client.render.VertexFormats
 import net.minecraft.client.texture.Sprite
 import net.minecraft.client.util.math.MatrixStack
-import net.minecraft.client.util.math.MatrixStack.Entry
 import net.minecraft.text.Text
 import net.minecraft.util.Identifier
 import net.minecraft.util.math.BlockPos
@@ -166,7 +165,7 @@ class RenderInWorldContext private constructor(
 
     companion object {
         private fun doLine(
-            matrix: Entry,
+            matrix: MatrixStack.Entry,
             buf: BufferBuilder,
             i: Number,
             j: Number,
@@ -179,9 +178,9 @@ class RenderInWorldContext private constructor(
                 .sub(i.toFloat(), j.toFloat(), k.toFloat())
                 .normalize()
             buf.vertex(matrix.positionMatrix, i.toFloat(), j.toFloat(), k.toFloat())
-                .normal(matrix.normalMatrix, normal.x, normal.y, normal.z).next()
+                .normal(matrix, normal.x, normal.y, normal.z).next()
             buf.vertex(matrix.positionMatrix, x.toFloat(), y.toFloat(), z.toFloat())
-                .normal(matrix.normalMatrix, normal.x, normal.y, normal.z).next()
+                .normal(matrix, normal.x, normal.y, normal.z).next()
         }
 
 
-- 
cgit