diff --git a/patches/api/Add-FeatureFlag-API.patch b/patches/api/Add-FeatureFlag-API.patch
index 9eccd985f2..cb94bed504 100644
--- a/patches/api/Add-FeatureFlag-API.patch
+++ b/patches/api/Add-FeatureFlag-API.patch
@@ -251,7 +251,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/org/bukkit/UnsafeValues.java
 +++ b/src/main/java/org/bukkit/UnsafeValues.java
 @@ -0,0 +0,0 @@ public interface UnsafeValues {
- 
+     @Deprecated(since = "1.21.3", forRemoval = true)
      String getTranslationKey(Attribute attribute);
  
 -    @Nullable
diff --git a/patches/api/Add-NamespacedKey-biome-methods.patch b/patches/api/Add-NamespacedKey-biome-methods.patch
index b03bd574c7..988dde154f 100644
--- a/patches/api/Add-NamespacedKey-biome-methods.patch
+++ b/patches/api/Add-NamespacedKey-biome-methods.patch
@@ -22,9 +22,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param x X-coordinate of the block
 +     * @param y Y-coordinate of the block
 +     * @param z Z-coordinate of the block
++     * @deprecated custom biomes are properly supported in API now
 +     * @return the biome's {@link NamespacedKey}
 +     */
 +    @org.jetbrains.annotations.NotNull
++    @Deprecated(since = "1.21.3", forRemoval = true)
 +    NamespacedKey getBiomeKey(RegionAccessor accessor, int x, int y, int z);
 +
 +    /**
@@ -38,8 +40,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param y Y-coordinate of the block
 +     * @param z Z-coordinate of the block
 +     * @param biomeKey Biome key
++     * @deprecated custom biomes are properly supported in API now
 +     * @throws IllegalStateException if no biome by the given key is registered.
 +     */
++    @Deprecated(since = "1.21.3", forRemoval = true)
 +    void setBiomeKey(RegionAccessor accessor, int x, int y, int z, NamespacedKey biomeKey);
 +    // Paper end - namespaced key biome methods
  }
diff --git a/patches/api/Add-RegistryAccess-for-managing-registries.patch b/patches/api/Add-RegistryAccess-for-managing-registries.patch
index 0b128edeb0..e7bb459f7a 100644
--- a/patches/api/Add-RegistryAccess-for-managing-registries.patch
+++ b/patches/api/Add-RegistryAccess-for-managing-registries.patch
@@ -211,6 +211,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/org/bukkit/Registry.java
 +++ b/src/main/java/org/bukkit/Registry.java
 @@ -0,0 +0,0 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
+      * Attribute.
+      *
+      * @see Attribute
++     * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#ATTRIBUTE}
+      */
+-    Registry<Attribute> ATTRIBUTE = Objects.requireNonNull(Bukkit.getRegistry(Attribute.class), "No registry present for Attribute. This is a bug.");
++    @Deprecated(since = "1.21.3") // Paper
++    Registry<Attribute> ATTRIBUTE = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Attribute.class), "No registry present for Attribute. This is a bug.");
+     /**
       * Server banner patterns.
       *
       * @see PatternType
@@ -222,6 +231,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      /**
       * Server biomes.
       *
+      * @see Biome
++     * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#BIOME}
+      */
+-    Registry<Biome> BIOME = Objects.requireNonNull(Bukkit.getRegistry(Biome.class), "No registry present for Biome. This is a bug.");
++    @Deprecated(since = "1.21.3") // Paper
++    Registry<Biome> BIOME = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Biome.class), "No registry present for Biome. This is a bug.");
+     /**
+      * Server block types.
+      *
 @@ -0,0 +0,0 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
       * @apiNote BlockType is not ready for public usage yet
       */
@@ -358,6 +376,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
       * Memory Keys.
       *
 @@ -0,0 +0,0 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
+      * Server fluids.
+      *
+      * @see Fluid
++     * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#FLUID}
+      */
+-    Registry<Fluid> FLUID = Objects.requireNonNull(Bukkit.getRegistry(Fluid.class), "No registry present for Fluid. This is a bug.");
++    @Deprecated(since = "1.21.3")
++    Registry<Fluid> FLUID = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Fluid.class), "No registry present for Fluid. This is a bug.");
+     /**
+      * Frog variants.
       *
       * @see Frog.Variant
       */
diff --git a/patches/api/Add-methods-to-get-translation-keys.patch b/patches/api/Add-methods-to-get-translation-keys.patch
index 1ffd838fb4..0a3a6ab4e9 100644
--- a/patches/api/Add-methods-to-get-translation-keys.patch
+++ b/patches/api/Add-methods-to-get-translation-keys.patch
@@ -242,46 +242,33 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  /**
   * Types of attributes which may be present on an {@link Attributable}.
   */
--public enum Attribute implements Keyed, Translatable {
-+public enum Attribute implements Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - Adventure translations
+-public interface Attribute extends OldEnum<Attribute>, Keyed, Translatable {
++public interface Attribute extends OldEnum<Attribute>, Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - Adventure translations
  
      /**
       * Maximum health of an Entity.
-@@ -0,0 +0,0 @@ public enum Attribute implements Keyed, Translatable {
-     public String getTranslationKey() {
-         return Bukkit.getUnsafe().getTranslationKey(this);
-     }
-+
-+    // Paper start
-+    @SuppressWarnings("deprecation")
-+    @Override
-+    public @NotNull String translationKey() {
-+        return Bukkit.getUnsafe().getTranslationKey(this);
-+    }
-+    // Paper end
- }
 diff --git a/src/main/java/org/bukkit/block/Biome.java b/src/main/java/org/bukkit/block/Biome.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/block/Biome.java
 +++ b/src/main/java/org/bukkit/block/Biome.java
 @@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
- /**
-  * Holds all accepted Biomes in the default server
+  * There may be additional biomes present in the server, for example from a {@link DataPack}
+  * which can be accessed via {@link Registry#BIOME}.
   */
--public enum Biome implements Keyed {
-+public enum Biome implements Keyed, net.kyori.adventure.translation.Translatable { // Paper
-     OCEAN,
-     PLAINS,
-     DESERT,
-@@ -0,0 +0,0 @@ public enum Biome implements Keyed {
-     public NamespacedKey getKey() {
-         return key;
+-public interface Biome extends OldEnum<Biome>, Keyed {
++public interface Biome extends OldEnum<Biome>, Keyed, net.kyori.adventure.translation.Translatable { // Paper - Adventure translations
+ 
+     Biome OCEAN = getBiome("ocean");
+     Biome PLAINS = getBiome("plains");
+@@ -0,0 +0,0 @@ public interface Biome extends OldEnum<Biome>, Keyed {
+     static Biome[] values() {
+         return Lists.newArrayList(Registry.BIOME).toArray(new Biome[0]);
      }
 +
 +    // Paper start
 +    @Override
-+    public @NotNull String translationKey() {
-+        return "biome.minecraft." + this.key.getKey();
++    default @NotNull String translationKey() {
++        return "biome.minecraft." + this.getKey().getKey();
 +    }
 +    // Paper end
  }
diff --git a/patches/api/Add-missing-Fluid-type.patch b/patches/api/Add-missing-Fluid-type.patch
deleted file mode 100644
index 570bc121bd..0000000000
--- a/patches/api/Add-missing-Fluid-type.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <jake.m.potrebic@gmail.com>
-Date: Sun, 4 Dec 2022 10:07:16 -0800
-Subject: [PATCH] Add missing Fluid type
-
-
-diff --git a/src/main/java/org/bukkit/Fluid.java b/src/main/java/org/bukkit/Fluid.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/org/bukkit/Fluid.java
-+++ b/src/main/java/org/bukkit/Fluid.java
-@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
-  */
- public enum Fluid implements Keyed {
- 
-+    // Paper start
-+    /**
-+     * No fluid.
-+     */
-+    EMPTY,
-+    // Paper end
-     /**
-      * Stationary water.
-      */
diff --git a/patches/api/Fix-custom-statistic-criteria-creation.patch b/patches/api/Fix-custom-statistic-criteria-creation.patch
index 7d4a07dff5..9e43959f25 100644
--- a/patches/api/Fix-custom-statistic-criteria-creation.patch
+++ b/patches/api/Fix-custom-statistic-criteria-creation.patch
@@ -9,7 +9,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/org/bukkit/UnsafeValues.java
 +++ b/src/main/java/org/bukkit/UnsafeValues.java
 @@ -0,0 +0,0 @@ public interface UnsafeValues {
-      */
+     @Deprecated(since = "1.21.3", forRemoval = true)
      void setBiomeKey(RegionAccessor accessor, int x, int y, int z, NamespacedKey biomeKey);
      // Paper end - namespaced key biome methods
 +
diff --git a/patches/api/Fix-upstream-javadocs.patch b/patches/api/Fix-upstream-javadocs.patch
index 74e963cea9..38105436ab 100644
--- a/patches/api/Fix-upstream-javadocs.patch
+++ b/patches/api/Fix-upstream-javadocs.patch
@@ -600,9 +600,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * Setting the size of the slime (regardless of previous size)
 +     * will set the following attributes:
 +     * <ul>
-+     *     <li>{@link org.bukkit.attribute.Attribute#GENERIC_MAX_HEALTH}</li>
-+     *     <li>{@link org.bukkit.attribute.Attribute#GENERIC_MOVEMENT_SPEED}</li>
-+     *     <li>{@link org.bukkit.attribute.Attribute#GENERIC_ATTACK_DAMAGE}</li>
++     *     <li>{@link org.bukkit.attribute.Attribute#MAX_HEALTH}</li>
++     *     <li>{@link org.bukkit.attribute.Attribute#MOVEMENT_SPEED}</li>
++     *     <li>{@link org.bukkit.attribute.Attribute#ATTACK_DAMAGE}</li>
 +     * </ul>
 +     * to their per-size defaults and heal the
 +     * slime to its max health (assuming it's alive).
diff --git a/patches/api/Improve-death-events.patch b/patches/api/Improve-death-events.patch
index 34e847f6f5..2a0ba2e835 100644
--- a/patches/api/Improve-death-events.patch
+++ b/patches/api/Improve-death-events.patch
@@ -79,7 +79,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @throws IllegalArgumentException Thrown if the health is {@literal <= 0 or >} max health
 +     */
 +    public void setReviveHealth(double reviveHealth) throws IllegalArgumentException {
-+        double maxHealth = ((LivingEntity) entity).getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue();
++        double maxHealth = ((LivingEntity) entity).getAttribute(org.bukkit.attribute.Attribute.MAX_HEALTH).getValue();
 +        if ((maxHealth != 0 && reviveHealth <= 0) || (reviveHealth > maxHealth)) {
 +            throw new IllegalArgumentException("Health must be between 0 (exclusive) and " + maxHealth + " (inclusive), but was " + reviveHealth);
 +        }
diff --git a/patches/api/Paper-Plugins.patch b/patches/api/Paper-Plugins.patch
index cf26d84991..2dc3870b7e 100644
--- a/patches/api/Paper-Plugins.patch
+++ b/patches/api/Paper-Plugins.patch
@@ -1331,7 +1331,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public interface UnsafeValues {
  
      @ApiStatus.Internal
-     <B extends Keyed> B get(Registry<B> registry, NamespacedKey key);
+     Biome getCustomBiome();
 +
 +    // Paper start
 +    @Deprecated(forRemoval = true)
diff --git a/patches/server/Add-FeatureFlag-API.patch b/patches/server/Add-FeatureFlag-API.patch
index 13471b9233..112b0a097c 100644
--- a/patches/server/Add-FeatureFlag-API.patch
+++ b/patches/server/Add-FeatureFlag-API.patch
@@ -287,17 +287,17 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
 +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-@@ -0,0 +0,0 @@ import org.bukkit.advancement.Advancement;
- import org.bukkit.attribute.Attribute;
+@@ -0,0 +0,0 @@ import org.bukkit.attribute.Attribute;
  import org.bukkit.attribute.AttributeModifier;
+ import org.bukkit.block.Biome;
  import org.bukkit.block.data.BlockData;
 -import org.bukkit.craftbukkit.CraftFeatureFlag;
 +// import org.bukkit.craftbukkit.CraftFeatureFlag; // Paper
  import org.bukkit.craftbukkit.CraftRegistry;
  import org.bukkit.craftbukkit.CraftServer;
- import org.bukkit.craftbukkit.attribute.CraftAttribute;
+ import org.bukkit.craftbukkit.block.CraftBiome;
 @@ -0,0 +0,0 @@ public final class CraftMagicNumbers implements UnsafeValues {
-         return CraftAttribute.bukkitToMinecraft(attribute).getDescriptionId();
+         return attribute.getTranslationKey();
      }
  
 -    @Override
diff --git a/patches/server/Add-NamespacedKey-biome-methods.patch b/patches/server/Add-NamespacedKey-biome-methods.patch
index abefd86fda..b324c7aed0 100644
--- a/patches/server/Add-NamespacedKey-biome-methods.patch
+++ b/patches/server/Add-NamespacedKey-biome-methods.patch
@@ -16,15 +16,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +    @Override
 +    public org.bukkit.NamespacedKey getBiomeKey(org.bukkit.RegionAccessor accessor, int x, int y, int z) {
-+        org.bukkit.craftbukkit.CraftRegionAccessor cra = (org.bukkit.craftbukkit.CraftRegionAccessor) accessor;
-+        return org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(cra.getHandle().registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME).getKey(cra.getHandle().getBiome(new net.minecraft.core.BlockPos(x, y, z)).value()));
++        return accessor.getBiome(x, y, z).getKey();
 +    }
 +
 +    @Override
 +    public void setBiomeKey(org.bukkit.RegionAccessor accessor, int x, int y, int z, org.bukkit.NamespacedKey biomeKey) {
-+        org.bukkit.craftbukkit.CraftRegionAccessor cra = (org.bukkit.craftbukkit.CraftRegionAccessor) accessor;
-+        net.minecraft.core.Holder<net.minecraft.world.level.biome.Biome> biomeBase = cra.getHandle().registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME).getOrThrow(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.BIOME, org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(biomeKey)));
-+        cra.setBiome(x, y, z, biomeBase);
++        accessor.setBiome(x, y, z, org.bukkit.Registry.BIOME.getOrThrow(biomeKey));
 +    }
      // Paper end
  
diff --git a/patches/server/Add-RegistryAccess-for-managing-Registries.patch b/patches/server/Add-RegistryAccess-for-managing-Registries.patch
index 05313f833a..6f2900e648 100644
--- a/patches/server/Add-RegistryAccess-for-managing-Registries.patch
+++ b/patches/server/Add-RegistryAccess-for-managing-Registries.patch
@@ -28,15 +28,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import net.minecraft.core.Registry;
 +import net.minecraft.core.registries.Registries;
 +import net.minecraft.resources.ResourceKey;
++import org.bukkit.Fluid;
 +import org.bukkit.GameEvent;
 +import org.bukkit.JukeboxSong;
 +import org.bukkit.Keyed;
 +import org.bukkit.MusicInstrument;
++import org.bukkit.attribute.Attribute;
++import org.bukkit.block.Biome;
 +import org.bukkit.block.BlockType;
 +import org.bukkit.block.banner.PatternType;
++import org.bukkit.craftbukkit.CraftFluid;
 +import org.bukkit.craftbukkit.CraftGameEvent;
 +import org.bukkit.craftbukkit.CraftJukeboxSong;
 +import org.bukkit.craftbukkit.CraftMusicInstrument;
++import org.bukkit.craftbukkit.attribute.CraftAttribute;
++import org.bukkit.craftbukkit.block.CraftBiome;
 +import org.bukkit.craftbukkit.block.CraftBlockType;
 +import org.bukkit.craftbukkit.block.banner.CraftPatternType;
 +import org.bukkit.craftbukkit.damage.CraftDamageType;
@@ -98,6 +104,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            entry(Registries.VILLAGER_TYPE, RegistryKey.VILLAGER_TYPE, Villager.Type.class, CraftVillager.CraftType::new),
 +            entry(Registries.MAP_DECORATION_TYPE, RegistryKey.MAP_DECORATION_TYPE, MapCursor.Type.class, CraftMapCursor.CraftType::new),
 +            entry(Registries.MENU, RegistryKey.MENU, MenuType.class, CraftMenuType::new),
++            entry(Registries.ATTRIBUTE, RegistryKey.ATTRIBUTE, Attribute.class, CraftAttribute::new),
++            entry(Registries.FLUID, RegistryKey.FLUID, Fluid.class, CraftFluid::new),
 +
 +            // data-drivens
 +            entry(Registries.STRUCTURE, RegistryKey.STRUCTURE, Structure.class, CraftStructure::new).delayed(),
@@ -108,17 +116,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            entry(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT, Enchantment.class, CraftEnchantment::new).withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(),
 +            entry(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG, JukeboxSong.class, CraftJukeboxSong::new).delayed(),
 +            entry(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN, PatternType.class, CraftPatternType::new).delayed(),
++            entry(Registries.BIOME, RegistryKey.BIOME, Biome.class, CraftBiome::new).delayed(),
 +
 +            // api-only
-+            apiOnly(Registries.BIOME, RegistryKey.BIOME, () -> org.bukkit.Registry.BIOME),
 +            apiOnly(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT, () -> org.bukkit.Registry.ART),
-+            apiOnly(Registries.ATTRIBUTE, RegistryKey.ATTRIBUTE, () -> org.bukkit.Registry.ATTRIBUTE),
 +            apiOnly(Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE, () -> org.bukkit.Registry.ENTITY_TYPE),
 +            apiOnly(Registries.PARTICLE_TYPE, RegistryKey.PARTICLE_TYPE, () -> org.bukkit.Registry.PARTICLE_TYPE),
 +            apiOnly(Registries.POTION, RegistryKey.POTION, () -> org.bukkit.Registry.POTION),
 +            apiOnly(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT, () -> org.bukkit.Registry.SOUNDS),
-+            apiOnly(Registries.MEMORY_MODULE_TYPE, RegistryKey.MEMORY_MODULE_TYPE, () -> (org.bukkit.Registry<MemoryKey<?>>) (org.bukkit.Registry) org.bukkit.Registry.MEMORY_MODULE_TYPE),
-+            apiOnly(Registries.FLUID, RegistryKey.FLUID, () -> org.bukkit.Registry.FLUID)
++            apiOnly(Registries.MEMORY_MODULE_TYPE, RegistryKey.MEMORY_MODULE_TYPE, () -> (org.bukkit.Registry<MemoryKey<?>>) (org.bukkit.Registry) org.bukkit.Registry.MEMORY_MODULE_TYPE)
 +        );
 +        final Map<RegistryKey<?>, RegistryEntry<?, ?>> byRegistryKey = new IdentityHashMap<>(REGISTRY_ENTRIES.size());
 +        final Map<ResourceKey<?>, RegistryEntry<?, ?>> byResourceKey = new IdentityHashMap<>(REGISTRY_ENTRIES.size());
@@ -758,9 +764,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -     * @return the bukkit registry of the provided class
 -     */
 -    public static <B extends Keyed> Registry<?> createRegistry(Class<? super B> bukkitClass, RegistryAccess registryHolder) {
+-        if (bukkitClass == Attribute.class) {
+-            return new CraftRegistry<>(Attribute.class, registryHolder.lookupOrThrow(Registries.ATTRIBUTE), CraftAttribute::new, FieldRename.ATTRIBUTE_RENAME);
+-        }
+-        if (bukkitClass == Biome.class) {
+-            return new CraftRegistry<>(Biome.class, registryHolder.lookupOrThrow(Registries.BIOME), CraftBiome::new, FieldRename.BIOME_RENAME);
+-        }
 -        if (bukkitClass == Enchantment.class) {
 -            return new CraftRegistry<>(Enchantment.class, registryHolder.lookupOrThrow(Registries.ENCHANTMENT), CraftEnchantment::new, FieldRename.ENCHANTMENT_RENAME);
 -        }
+-        if (bukkitClass == Fluid.class) {
+-            return new CraftRegistry<>(Fluid.class, registryHolder.lookupOrThrow(Registries.FLUID), CraftFluid::new, FieldRename.NONE);
+-        }
 -        if (bukkitClass == GameEvent.class) {
 -            return new CraftRegistry<>(GameEvent.class, registryHolder.lookupOrThrow(Registries.GAME_EVENT), CraftGameEvent::new, FieldRename.NONE);
 -        }
@@ -1260,7 +1275,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      static {
 -        // Order: Bukkit class, Minecraft Registry key, CraftBukkit class, Minecraft class
+-        register(Attribute.class, Registries.ATTRIBUTE, CraftAttribute.class, net.minecraft.world.entity.ai.attributes.Attribute.class);
+-        register(Biome.class, Registries.BIOME, CraftBiome.class, net.minecraft.world.level.biome.Biome.class);
 -        register(Enchantment.class, Registries.ENCHANTMENT, CraftEnchantment.class, net.minecraft.world.item.enchantment.Enchantment.class);
+-        register(Fluid.class, Registries.FLUID, CraftFluid.class, net.minecraft.world.level.material.Fluid.class);
 -        register(GameEvent.class, Registries.GAME_EVENT, CraftGameEvent.class, net.minecraft.world.level.gameevent.GameEvent.class);
 -        register(MusicInstrument.class, Registries.INSTRUMENT, CraftMusicInstrument.class, Instrument.class);
 -        register(MenuType.class, Registries.MENU, CraftMenuType.class, net.minecraft.world.inventory.MenuType.class);
@@ -1282,7 +1300,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        register(PatternType.class, Registries.BANNER_PATTERN, CraftPatternType.class, BannerPattern.class);
 -
 +        // Order: RegistryKey, Bukkit class, Minecraft Registry key, CraftBukkit class, Minecraft class
++        register(RegistryKey.ATTRIBUTE, Attribute.class, Registries.ATTRIBUTE, CraftAttribute.class, net.minecraft.world.entity.ai.attributes.Attribute.class);
++        register(RegistryKey.BIOME, Biome.class, Registries.BIOME, CraftBiome.class, net.minecraft.world.level.biome.Biome.class);
 +        register(RegistryKey.ENCHANTMENT, Enchantment.class, Registries.ENCHANTMENT, CraftEnchantment.class, net.minecraft.world.item.enchantment.Enchantment.class);
++        register(RegistryKey.FLUID, Fluid.class, Registries.FLUID, CraftFluid.class, net.minecraft.world.level.material.Fluid.class);
 +        register(RegistryKey.GAME_EVENT, GameEvent.class, Registries.GAME_EVENT, CraftGameEvent.class, net.minecraft.world.level.gameevent.GameEvent.class);
 +        register(RegistryKey.INSTRUMENT, MusicInstrument.class, Registries.INSTRUMENT, CraftMusicInstrument.class, Instrument.class);
 +        register(RegistryKey.MOB_EFFECT, PotionEffectType.class, Registries.MOB_EFFECT, CraftPotionEffectType.class, MobEffect.class);
diff --git a/patches/server/Add-methods-to-get-translation-keys.patch b/patches/server/Add-methods-to-get-translation-keys.patch
index 4cc6306cb1..f29e4836e1 100644
--- a/patches/server/Add-methods-to-get-translation-keys.patch
+++ b/patches/server/Add-methods-to-get-translation-keys.patch
@@ -10,6 +10,22 @@ public org.bukkit.craftbukkit.inventory.CraftMetaFirework getNBT(Lorg/bukkit/Fir
 
 Co-authored-by: MeFisto94 <MeFisto94@users.noreply.github.com>
 
+diff --git a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java
++++ b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java
+@@ -0,0 +0,0 @@ public class CraftAttribute implements Attribute, Handleable<net.minecraft.world
+         return this.attributeBase.getDescriptionId();
+     }
+ 
++    @Override
++    public @NotNull String translationKey() {
++        return this.attributeBase.getDescriptionId();
++    }
++
+     @Override
+     public int compareTo(@NotNull Attribute attribute) {
+         return this.ordinal - attribute.ordinal();
 diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
diff --git a/patches/server/Add-registry-entry-and-builders.patch b/patches/server/Add-registry-entry-and-builders.patch
index 4b0dd38c84..59295a1587 100644
--- a/patches/server/Add-registry-entry-and-builders.patch
+++ b/patches/server/Add-registry-entry-and-builders.patch
@@ -34,7 +34,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            writable(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT, Enchantment.class, CraftEnchantment::new, PaperEnchantmentRegistryEntry.PaperBuilder::new).withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(),
              entry(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG, JukeboxSong.class, CraftJukeboxSong::new).delayed(),
              entry(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN, PatternType.class, CraftPatternType::new).delayed(),
- 
+             entry(Registries.BIOME, RegistryKey.BIOME, Biome.class, CraftBiome::new).delayed(),
 diff --git a/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java
 new file mode 100644
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
diff --git a/patches/server/CB-fixes.patch b/patches/server/CB-fixes.patch
index 3c66729fab..b91036b94a 100644
--- a/patches/server/CB-fixes.patch
+++ b/patches/server/CB-fixes.patch
@@ -31,19 +31,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.structureManager = new StructureManager(this, this.serverLevelData.worldGenOptions(), this.structureCheck); // CraftBukkit
          if ((this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) || env == org.bukkit.World.Environment.THE_END) { // CraftBukkit - Allow to create EnderDragonBattle in default and custom END
              this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit
-diff --git a/src/main/java/net/minecraft/world/item/enchantment/effects/SummonEntityEffect.java b/src/main/java/net/minecraft/world/item/enchantment/effects/SummonEntityEffect.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/world/item/enchantment/effects/SummonEntityEffect.java
-+++ b/src/main/java/net/minecraft/world/item/enchantment/effects/SummonEntityEffect.java
-@@ -0,0 +0,0 @@ public record SummonEntityEffect(HolderSet<EntityType<?>> entityTypes, boolean j
-                         // CraftBukkit start
-                         world.strikeLightning(entity1, (context.itemStack().getItem() == Items.TRIDENT) ? LightningStrikeEvent.Cause.TRIDENT : LightningStrikeEvent.Cause.ENCHANTMENT);
-                     } else {
--                        world.addFreshEntityWithPassengers(user, CreatureSpawnEvent.SpawnReason.ENCHANTMENT);
-+                        world.addFreshEntityWithPassengers(entity1, CreatureSpawnEvent.SpawnReason.ENCHANTMENT); // Paper - Fix typo when adding summoned entity
-                         // CraftBukkit end
-                     }
- 
 diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/StructureCheck.java b/src/main/java/net/minecraft/world/level/levelgen/structure/StructureCheck.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/world/level/levelgen/structure/StructureCheck.java
diff --git a/patches/server/Cache-resource-keys-and-optimize-reference-Holder-ta.patch b/patches/server/Cache-resource-keys-and-optimize-reference-Holder-ta.patch
index 7feb0672f3..35f04d82f1 100644
--- a/patches/server/Cache-resource-keys-and-optimize-reference-Holder-ta.patch
+++ b/patches/server/Cache-resource-keys-and-optimize-reference-Holder-ta.patch
@@ -18,34 +18,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          }
  
          @Override
-diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBiome.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBiome.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/org/bukkit/craftbukkit/block/CraftBiome.java
-+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBiome.java
-@@ -0,0 +0,0 @@ package org.bukkit.craftbukkit.block;
- import com.google.common.base.Preconditions;
- import net.minecraft.core.Holder;
- import net.minecraft.core.registries.Registries;
-+import net.minecraft.resources.ResourceKey;
- import org.bukkit.Registry;
- import org.bukkit.block.Biome;
- import org.bukkit.craftbukkit.CraftRegistry;
-@@ -0,0 +0,0 @@ public class CraftBiome {
-         return CraftBiome.minecraftToBukkit(minecraft.value());
-     }
- 
-+    private static final java.util.Map<org.bukkit.block.Biome, ResourceKey<net.minecraft.world.level.biome.Biome>> BIOME_KEY_CACHE = java.util.Collections.synchronizedMap(new java.util.EnumMap<>(Biome.class)); // Paper
-     public static net.minecraft.world.level.biome.Biome bukkitToMinecraft(Biome bukkit) {
-         if (bukkit == null || bukkit == Biome.CUSTOM) {
-             return null;
-         }
- 
-         return CraftRegistry.getMinecraftRegistry(Registries.BIOME)
--                .getOptional(CraftNamespacedKey.toMinecraft(bukkit.getKey())).orElseThrow();
-+                .getOptional(BIOME_KEY_CACHE.computeIfAbsent(bukkit, b -> ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(b.getKey())))).orElseThrow();
-     }
- 
-     public static Holder<net.minecraft.world.level.biome.Biome> bukkitToMinecraftHolder(Biome bukkit) {
 diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java
diff --git a/patches/server/Expose-server-build-information.patch b/patches/server/Expose-server-build-information.patch
index 478f0836cc..fcea6472d0 100644
--- a/patches/server/Expose-server-build-information.patch
+++ b/patches/server/Expose-server-build-information.patch
@@ -696,7 +696,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
 +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
 @@ -0,0 +0,0 @@ public final class CraftMagicNumbers implements UnsafeValues {
-         return CraftRegistry.get(registry, namespacedKey, ApiVersion.CURRENT);
+         return this.customBiome;
      }
  
 +    // Paper start
diff --git a/patches/server/Fix-custom-statistic-criteria-creation.patch b/patches/server/Fix-custom-statistic-criteria-creation.patch
index 840bf593c6..ebd21f1b8f 100644
--- a/patches/server/Fix-custom-statistic-criteria-creation.patch
+++ b/patches/server/Fix-custom-statistic-criteria-creation.patch
@@ -9,8 +9,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
 +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
 @@ -0,0 +0,0 @@ public final class CraftMagicNumbers implements UnsafeValues {
-         net.minecraft.core.Holder<net.minecraft.world.level.biome.Biome> biomeBase = cra.getHandle().registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME).getOrThrow(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.BIOME, org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(biomeKey)));
-         cra.setBiome(x, y, z, biomeBase);
+     public void setBiomeKey(org.bukkit.RegionAccessor accessor, int x, int y, int z, org.bukkit.NamespacedKey biomeKey) {
+         accessor.setBiome(x, y, z, org.bukkit.Registry.BIOME.getOrThrow(biomeKey));
      }
 +
 +    @Override
diff --git a/patches/server/Fixup-NamespacedKey-handling.patch b/patches/server/Fixup-NamespacedKey-handling.patch
index 0786dc0c20..76e2ddd23a 100644
--- a/patches/server/Fixup-NamespacedKey-handling.patch
+++ b/patches/server/Fixup-NamespacedKey-handling.patch
@@ -42,7 +42,7 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java
 +++ b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java
-@@ -0,0 +0,0 @@ public class CraftAttribute {
+@@ -0,0 +0,0 @@ public class CraftAttribute implements Attribute, Handleable<net.minecraft.world
          string = FieldRename.convertAttributeName(ApiVersion.CURRENT, string);
          string = string.toLowerCase(Locale.ROOT);
          NamespacedKey key = NamespacedKey.fromString(string);
diff --git a/patches/server/General-ItemMeta-fixes.patch b/patches/server/General-ItemMeta-fixes.patch
index 75e2e59347..97ecb492f3 100644
--- a/patches/server/General-ItemMeta-fixes.patch
+++ b/patches/server/General-ItemMeta-fixes.patch
@@ -1193,15 +1193,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        return (this.hasUseCooldown()) ? new CraftUseCooldownComponent(this.useCooldown) : new CraftUseCooldownComponent(new UseCooldown(1.0F)); // Paper - Create a valid use_cooldown component
      }
  
-     @Override
-@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- 
-     @Override
-     public void setEquippable(EquippableComponent equippable) {
--        this.equippable = (equippable == null) ? null : new CraftEquippableComponent((CraftEquippableComponent) this.equippable);
-+        this.equippable = (equippable == null) ? null : new CraftEquippableComponent((CraftEquippableComponent) equippable); // Paper
-     }
- 
      @Override
 @@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
  
diff --git a/patches/server/Get-entity-default-attributes.patch b/patches/server/Get-entity-default-attributes.patch
index d61a9db855..b02f49e17a 100644
--- a/patches/server/Get-entity-default-attributes.patch
+++ b/patches/server/Get-entity-default-attributes.patch
@@ -142,8 +142,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    @Test
 +    public void testUnmodifiabilityOfAttributable() {
 +        Attributable attributable = EntityType.ZOMBIE.getDefaultAttributes();
-+        assertThrows(UnsupportedOperationException.class, () -> attributable.registerAttribute(Attribute.GENERIC_ATTACK_DAMAGE));
-+        AttributeInstance instance = attributable.getAttribute(Attribute.GENERIC_FOLLOW_RANGE);
++        assertThrows(UnsupportedOperationException.class, () -> attributable.registerAttribute(Attribute.ATTACK_DAMAGE));
++        AttributeInstance instance = attributable.getAttribute(Attribute.FOLLOW_RANGE);
 +        assertNotNull(instance);
 +        assertThrows(UnsupportedOperationException.class, () -> instance.addModifier(new AttributeModifier("test", 3, AttributeModifier.Operation.ADD_NUMBER)));
 +        assertThrows(UnsupportedOperationException.class, () -> instance.setBaseValue(3.2));
diff --git a/patches/server/Improve-death-events.patch b/patches/server/Improve-death-events.patch
index 57a4612990..beefb52fa6 100644
--- a/patches/server/Improve-death-events.patch
+++ b/patches/server/Improve-death-events.patch
@@ -481,7 +481,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    // Paper start - helper methods for making death event cancellable
 +    // Add information to death event
 +    private static void populateFields(net.minecraft.world.entity.LivingEntity victim, EntityDeathEvent event) {
-+        event.setReviveHealth(event.getEntity().getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue());
++        event.setReviveHealth(event.getEntity().getAttribute(org.bukkit.attribute.Attribute.MAX_HEALTH).getValue());
 +        event.setShouldPlayDeathSound(!victim.silentDeath && !victim.isSilent());
 +        net.minecraft.sounds.SoundEvent soundEffect = victim.getDeathSound();
 +        event.setDeathSound(soundEffect != null ? org.bukkit.craftbukkit.CraftSound.minecraftToBukkit(soundEffect) : null);
diff --git a/patches/server/Properly-track-the-changed-item-from-dispense-events.patch b/patches/server/Properly-track-the-changed-item-from-dispense-events.patch
index 7ff4ec03f3..fbe3b65e44 100644
--- a/patches/server/Properly-track-the-changed-item-from-dispense-events.patch
+++ b/patches/server/Properly-track-the-changed-item-from-dispense-events.patch
@@ -76,14 +76,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
 +++ b/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
 @@ -0,0 +0,0 @@ public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior {
-             }
-         }
  
--        Projectile iprojectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, stack, enumdirection), worldserver, stack, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty());
-+        Projectile iprojectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, CraftItemStack.unwrap(event.getItem()), enumdirection), worldserver, stack, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // Paper - track changed items in the dispense event; unwrap is safe here because all uses of the stack make their own copies
-         ((Entity) iprojectile).projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(pointer.blockEntity());
+         // SPIGOT-7923: Avoid create projectiles with empty item
+         if (!itemstack1.isEmpty()) {
+-            Projectile iprojectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, itemstack1, enumdirection), worldserver, itemstack1, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty());
++            Projectile iprojectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, CraftItemStack.unwrap(event.getItem()), enumdirection), worldserver, itemstack1, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // Paper - track changed items in the dispense event; unwrap is safe here because all uses of the stack make their own copies
+             iprojectile.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(pointer.blockEntity());
+         }
          // itemstack.shrink(1); // CraftBukkit - Handled during event processing
-         // CraftBukkit end
 diff --git a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
diff --git a/work/Bukkit b/work/Bukkit
index 553558256c..97c5926140 160000
--- a/work/Bukkit
+++ b/work/Bukkit
@@ -1 +1 @@
-Subproject commit 553558256cab26217919a0809cc26f7aad22995d
+Subproject commit 97c5926140420c7b9e8a06b2b8348b1816f2a6bd
diff --git a/work/CraftBukkit b/work/CraftBukkit
index 18b8ae1839..7235ad7b04 160000
--- a/work/CraftBukkit
+++ b/work/CraftBukkit
@@ -1 +1 @@
-Subproject commit 18b8ae18390763d48ea292435b75c1eabf5dbff7
+Subproject commit 7235ad7b04542ce214869eb1ec1966bb392aedc7