diff --git a/divinemc-api/paper-patches/features/0003-Expanded-Adventure-support.patch b/divinemc-api/paper-patches/features/0003-Expanded-Adventure-support.patch
new file mode 100644
index 0000000..3641c37
--- /dev/null
+++ b/divinemc-api/paper-patches/features/0003-Expanded-Adventure-support.patch
@@ -0,0 +1,77 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
+Date: Tue, 14 Jan 2025 19:49:49 +0300
+Subject: [PATCH] Expanded Adventure support
+
+Adds support for Adventure in a few places where it was previously missing.
+Original patch was taken from Parchment: https://github.com/ProjectEdenGG/Parchment
+
+diff --git a/src/main/java/org/bukkit/Color.java b/src/main/java/org/bukkit/Color.java
+index f8edb964c4af597b03a2de06c464cc06a96b791c..596e2e09c6a64fa5861221789185d2fd7b004248 100644
+--- a/src/main/java/org/bukkit/Color.java
++++ b/src/main/java/org/bukkit/Color.java
+@@ -17,7 +17,7 @@ import org.jetbrains.annotations.Nullable;
+ * but subject to change.
+ */
+ @SerializableAs("Color")
+-public final class Color implements ConfigurationSerializable {
++public final class Color implements ConfigurationSerializable, net.kyori.adventure.text.format.TextColor { // DivineMC - Expanded Adventure support
+ private static final int BIT_MASK = 0xff;
+ private static final int DEFAULT_ALPHA = 255;
+
+@@ -310,6 +310,13 @@ public final class Color implements ConfigurationSerializable {
+ return getAlpha() << 24 | getRed() << 16 | getGreen() << 8 | getBlue();
+ }
+
++ // DivineMC start - Expanded Adventure support
++ @Override
++ public int value() {
++ return asRGB();
++ }
++ // DivineMC end - Expanded Adventure support
++
+ /**
+ * Gets the color as an BGR integer.
+ *
+diff --git a/src/main/java/org/bukkit/DyeColor.java b/src/main/java/org/bukkit/DyeColor.java
+index 2f038f233afd4210687586800070d5f61e40562a..24068f23f45a0d3b837b04565d143a5cd8cd8869 100644
+--- a/src/main/java/org/bukkit/DyeColor.java
++++ b/src/main/java/org/bukkit/DyeColor.java
+@@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * All supported color values for dyes and cloth
+ */
+-public enum DyeColor {
++public enum DyeColor implements net.kyori.adventure.util.RGBLike, net.kyori.adventure.text.format.StyleBuilderApplicable { // DivineMC - Expanded Adventure support
+
+ /**
+ * Represents white dye.
+@@ -135,6 +135,28 @@ public enum DyeColor {
+ return firework;
+ }
+
++ // DivineMC start - Expanded Adventure support
++ @Override
++ public @org.jetbrains.annotations.Range(from = 0L, to = 255L) int red() {
++ return color.getRed();
++ }
++
++ @Override
++ public @org.jetbrains.annotations.Range(from = 0L, to = 255L) int green() {
++ return color.getGreen();
++ }
++
++ @Override
++ public @org.jetbrains.annotations.Range(from = 0L, to = 255L) int blue() {
++ return color.getBlue();
++ }
++
++ @Override
++ public void styleApply(net.kyori.adventure.text.format.Style.@org.jetbrains.annotations.NotNull Builder style) {
++ style.color(net.kyori.adventure.text.format.TextColor.color(color));
++ }
++ // DivineMC end - Expanded Adventure support
++
+ /**
+ * Gets the DyeColor with the given wool data value.
+ *
diff --git a/divinemc-api/paper-patches/features/0004-Extend-Location-API.patch b/divinemc-api/paper-patches/features/0004-Extend-Location-API.patch
new file mode 100644
index 0000000..5af0ef6
--- /dev/null
+++ b/divinemc-api/paper-patches/features/0004-Extend-Location-API.patch
@@ -0,0 +1,181 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
+Date: Tue, 14 Jan 2025 20:36:30 +0300
+Subject: [PATCH] Extend Location API
+
+
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index 8bc340c9d4d8d1b113d877e25af769ef9251dc94..1311f7c82e412aec415a7f2a581400b84f86df9b 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -1212,4 +1212,170 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
+ public @NotNull Location toLocation(@NotNull World world) {
+ return new Location(world, this.x(), this.y(), this.z(), this.getYaw(), this.getPitch());
+ }
++
++ // DivineMC start - Extend Location API
++ /**
++ * Sets the x-coordinate of this location.
++ *
++ * @param x The x-coordinate
++ * @return this location
++ */
++ public @NotNull Location x(double x) {
++ this.x = x;
++ return this;
++ }
++
++ /**
++ * Sets the y-coordinate of this location.
++ *
++ * @param y The y-coordinate
++ * @return this location
++ */
++ public @NotNull Location y(double y) {
++ this.y = y;
++ return this;
++ }
++
++ /**
++ * Sets the z-coordinate of this location.
++ *
++ * @param z The z-coordinate
++ * @return this location
++ */
++ public @NotNull Location z(double z) {
++ this.z = z;
++ return this;
++ }
++
++ /**
++ * Sets the yaw of this location, measured in degrees.
++ *
++ * - A yaw of 0 or 360 represents the positive z direction.
++ *
- A yaw of 180 represents the negative z direction.
++ *
- A yaw of 90 represents the negative x direction.
++ *
- A yaw of 270 represents the positive x direction.
++ *
++ * Increasing yaw values are the equivalent of turning to your
++ * right-facing, increasing the scale of the next respective axis, and
++ * decreasing the scale of the previous axis.
++ *
++ * @param yaw new rotation's yaw
++ * @return this location
++ */
++ public @NotNull Location yaw(float yaw) {
++ this.yaw = yaw;
++ return this;
++ }
++
++ /**
++ * Sets the pitch of this location, measured in degrees.
++ *
++ * - A pitch of 0 represents level forward facing.
++ *
- A pitch of 90 represents downward facing, or negative y
++ * direction.
++ *
- A pitch of -90 represents upward facing, or positive y direction.
++ *
++ * Increasing pitch values the equivalent of looking down.
++ *
++ * @param pitch new incline's pitch
++ * @return this location
++ */
++ public @NotNull Location pitch(float pitch) {
++ this.pitch = pitch;
++ return this;
++ }
++
++ /**
++ * Sets the world that this location resides in
++ *
++ * @param world New world that this location resides in
++ * @return this location
++ */
++ public @NotNull Location world(@Nullable World world) {
++ this.world = (world == null) ? null : new WeakReference<>(world);
++ return this;
++ }
++
++ /**
++ * Increments the x-coordinate by the given value.
++ *
++ * @param x Amount to increment the x-coordinate by
++ * @return this location
++ */
++ public @NotNull Location addX(double x) {
++ this.x += x;
++ return this;
++ }
++
++ /**
++ * Increments the y-coordinate by the given value.
++ *
++ * @param y Amount to increment the y-coordinate by
++ * @return this location
++ */
++ public @NotNull Location addY(double y) {
++ this.y += y;
++ return this;
++ }
++
++ /**
++ * Increments the z-coordinate by the given value.
++ *
++ * @param z Amount to increment the z-coordinate by
++ * @return this location
++ */
++ public @NotNull Location addZ(double z) {
++ this.z += z;
++ return this;
++ }
++
++ /**
++ * Returns location with centered X, Y and Z values.
++ *
++ * @return this location
++ */
++ public @NotNull Location center() {
++ return center(0.5);
++ }
++
++ /**
++ * Returns location with centered X and Z values.
++ * Y will be set to provided.
++ *
++ * @param y Y adding value
++ * @return this location
++ */
++ public @NotNull Location center(double y) {
++ return set(getBlockX() + 0.5, getBlockY() + y, getBlockZ() + 0.5);
++ }
++
++ /**
++ * Checks if locations have the same X, Y and Z values.
++ *
++ * @param loc Location to check
++ * @return true if locations have same coordinates
++ * @apiNote Ignores world
++ */
++ public boolean isSame(@NotNull Location loc) {
++ return getY() == loc.getY() && getX() == loc.getX() && getZ() == loc.getZ();
++ }
++
++ /**
++ * Shifts this location by one block up
++ *
++ * @return this location
++ */
++ public @NotNull Location above() {
++ return addY(1);
++ }
++
++ /**
++ * Shifts this location by one block down
++ *
++ * @return this location
++ */
++ public @NotNull Location below() {
++ return addY(-1);
++ }
++ // DivineMC end - Extend Location API
+ }
diff --git a/divinemc-api/paper-patches/features/0005-Extend-Sound-API.patch b/divinemc-api/paper-patches/features/0005-Extend-Sound-API.patch
new file mode 100644
index 0000000..171715f
--- /dev/null
+++ b/divinemc-api/paper-patches/features/0005-Extend-Sound-API.patch
@@ -0,0 +1,80 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
+Date: Tue, 14 Jan 2025 20:42:04 +0300
+Subject: [PATCH] Extend Sound API
+
+
+diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
+index b7530e7f389fdc6d815bdff0949fca4b14298c07..6e83e5cd66d38b14a79b54d4944eaec4fe30ef7e 100644
+--- a/src/main/java/org/bukkit/block/Block.java
++++ b/src/main/java/org/bukkit/block/Block.java
+@@ -820,4 +820,29 @@ public interface Block extends Metadatable, Translatable, net.kyori.adventure.tr
+ return this.getBlockData().getDestroySpeed(itemStack, considerEnchants);
+ }
+ // Paper end - destroy speed API
++
++ // DivineMC start - Extend Sound API
++ /**
++ * Plays a sound at the location of the block
++ *
++ * @param sound sound to play
++ * @param volume volume of the sound
++ * @param pitch pitch of the sound
++ */
++ default void emitSound(@NotNull org.bukkit.Sound sound, float volume, float pitch) {
++ emitSound(sound, org.bukkit.SoundCategory.BLOCKS, volume, pitch);
++ }
++
++ /**
++ * Plays a sound at the location of the block
++ *
++ * @param sound sound to play
++ * @param category category of the sound
++ * @param volume volume of the sound
++ * @param pitch pitch of the sound
++ */
++ default void emitSound(@NotNull org.bukkit.Sound sound, @NotNull org.bukkit.SoundCategory category, float volume, float pitch) {
++ getWorld().playSound(getLocation().toCenterLocation(), sound, category, volume, pitch);
++ }
++ // DivineMC end - Extend Sound API
+ }
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index 49d3ca54a761e08cfe1bc770cb879223bf0e21e8..942f7497a56baa8e717980795e605a8907039fe6 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -1251,4 +1251,35 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ */
+ void setImmuneToFire(@Nullable Boolean fireImmune);
+ // Purpur end - Fire Immunity API
++
++ // DivineMC start - Extend Sound API
++ /**
++ * Plays a sound at the location of the entity
++ *
++ * @param sound sound to play
++ * @param volume volume of the sound
++ * @param pitch pitch of the sound
++ */
++ default void emitSound(@NotNull org.bukkit.Sound sound, float volume, float pitch) {
++ org.bukkit.SoundCategory soundGroup = switch (this) {
++ case HumanEntity humanEntity -> org.bukkit.SoundCategory.PLAYERS;
++ case Ambient ambient -> org.bukkit.SoundCategory.AMBIENT;
++ case Monster monster -> org.bukkit.SoundCategory.HOSTILE;
++ default -> org.bukkit.SoundCategory.NEUTRAL;
++ };
++ emitSound(sound, soundGroup, volume, pitch);
++ }
++
++ /**
++ * Plays a sound at the location of the block
++ *
++ * @param sound sound to play
++ * @param category category of the sound
++ * @param volume volume of the sound
++ * @param pitch pitch of the sound
++ */
++ default void emitSound(@NotNull org.bukkit.Sound sound, @NotNull org.bukkit.SoundCategory category, float volume, float pitch) {
++ getWorld().playSound(this, sound, category, volume, pitch);
++ }
++ // DivineMC end - Extend Sound API
+ }
diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/Note.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/Note.java.patch
new file mode 100644
index 0000000..45db7c3
--- /dev/null
+++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/Note.java.patch
@@ -0,0 +1,73 @@
+--- a/src/main/java/org/bukkit/Note.java
++++ b/src/main/java/org/bukkit/Note.java
+@@ -127,6 +_,7 @@
+ }
+
+ private final byte note;
++ private final net.kyori.adventure.text.format.TextColor color; // DivineMC - Note Color API
+
+ /**
+ * Creates a new note.
+@@ -138,6 +_,7 @@
+ Preconditions.checkArgument(note >= 0 && note <= 24, "The note value has to be between 0 and 24.");
+
+ this.note = (byte) note;
++ this.color = getColor(note); // DivineMC - Note Color API
+ }
+
+ /**
+@@ -158,6 +_,7 @@
+ }
+
+ this.note = (byte) (octave * Tone.TONES_COUNT + tone.getId(sharped));
++ this.color = getColor(note); // DivineMC - Note Color API
+ }
+
+ /**
+@@ -298,4 +_,46 @@
+ public String toString() {
+ return "Note{" + getTone().toString() + (isSharped() ? "#" : "") + "}";
+ }
++
++ // DivineMC start - Note Color API
++ /**
++ * Get color of the played note.
++ *
++ * @return the color of the note
++ */
++ @NotNull
++ public net.kyori.adventure.text.format.TextColor getColor() {
++ return color;
++ }
++
++ private static @NotNull net.kyori.adventure.text.format.TextColor getColor(int note) {
++ return switch (note) {
++ case 0 -> net.kyori.adventure.text.format.TextColor.fromHexString("#77D700");
++ case 1 -> net.kyori.adventure.text.format.TextColor.fromHexString("#95C000");
++ case 2 -> net.kyori.adventure.text.format.TextColor.fromHexString("#B2A500");
++ case 3 -> net.kyori.adventure.text.format.TextColor.fromHexString("#CC8600");
++ case 4 -> net.kyori.adventure.text.format.TextColor.fromHexString("#E26500");
++ case 5 -> net.kyori.adventure.text.format.TextColor.fromHexString("#F34100");
++ case 6 -> net.kyori.adventure.text.format.TextColor.fromHexString("#FC1E00");
++ case 7 -> net.kyori.adventure.text.format.TextColor.fromHexString("#FE000F");
++ case 8 -> net.kyori.adventure.text.format.TextColor.fromHexString("#F70033");
++ case 9 -> net.kyori.adventure.text.format.TextColor.fromHexString("#E8005A");
++ case 10 -> net.kyori.adventure.text.format.TextColor.fromHexString("#CF0083");
++ case 11 -> net.kyori.adventure.text.format.TextColor.fromHexString("#AE00A9");
++ case 12 -> net.kyori.adventure.text.format.TextColor.fromHexString("#8600CC");
++ case 13 -> net.kyori.adventure.text.format.TextColor.fromHexString("#5B00E7");
++ case 14 -> net.kyori.adventure.text.format.TextColor.fromHexString("#2D00F9");
++ case 15 -> net.kyori.adventure.text.format.TextColor.fromHexString("#020AFE");
++ case 16 -> net.kyori.adventure.text.format.TextColor.fromHexString("#0037F6");
++ case 17 -> net.kyori.adventure.text.format.TextColor.fromHexString("#0068E0");
++ case 18 -> net.kyori.adventure.text.format.TextColor.fromHexString("#009ABC");
++ case 19 -> net.kyori.adventure.text.format.TextColor.fromHexString("#00C68D");
++ case 20 -> net.kyori.adventure.text.format.TextColor.fromHexString("#00E958");
++ case 21 -> net.kyori.adventure.text.format.TextColor.fromHexString("#00FC21");
++ case 22 -> net.kyori.adventure.text.format.TextColor.fromHexString("#1FFC00");
++ case 23 -> net.kyori.adventure.text.format.TextColor.fromHexString("#59E800");
++ default -> net.kyori.adventure.text.format.TextColor.fromHexString("#94C100");
++ };
++ }
++ // DivineMC end - Note Color API
+ }
diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/AbstractArrow.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/AbstractArrow.java.patch
new file mode 100644
index 0000000..22a6821
--- /dev/null
+++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/AbstractArrow.java.patch
@@ -0,0 +1,14 @@
+--- a/src/main/java/org/bukkit/entity/AbstractArrow.java
++++ b/src/main/java/org/bukkit/entity/AbstractArrow.java
+@@ -282,4 +_,11 @@
+ */
+ void setShooter(@Nullable org.bukkit.projectiles.ProjectileSource source, boolean resetPickupStatus);
+ // Paper end - Fix PickupStatus getting reset
++
++ // DivineMC start - Add startFalling method to AbstractArrow
++ /**
++ * Asks projectile to start falling if possible
++ */
++ void startFalling();
++ // DivineMC end - Add startFalling method to AbstractArrow
+ }
diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/Player.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/Player.java.patch
new file mode 100644
index 0000000..27bfd2a
--- /dev/null
+++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/Player.java.patch
@@ -0,0 +1,17 @@
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -4011,4 +_,14 @@
+ sendDeathScreen(message);
+ }
+ // Purpur end
++
++ // DivineMC start - Open Ender Chest API
++ /**
++ * Opens ender chest for the player
++ *
++ * @param enderChest ender chest
++ * @return whether the chest was opened
++ */
++ boolean openEnderChest(@NotNull org.bukkit.block.EnderChest enderChest);
++ // DivineMC end - Open Ender Chest API
+ }
diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/event/block/NotePlayEvent.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/event/block/NotePlayEvent.java.patch
new file mode 100644
index 0000000..0caf824
--- /dev/null
+++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/event/block/NotePlayEvent.java.patch
@@ -0,0 +1,43 @@
+--- a/src/main/java/org/bukkit/event/block/NotePlayEvent.java
++++ b/src/main/java/org/bukkit/event/block/NotePlayEvent.java
+@@ -16,13 +_,21 @@
+ private static HandlerList handlers = new HandlerList();
+ private Instrument instrument;
+ private Note note;
++ private final @org.jetbrains.annotations.Nullable org.bukkit.entity.Player player; // DivineMC - Add player to NotePlayEvent
+ private boolean cancelled = false;
+
++ // DivineMC start - Add player to NotePlayEvent
+ public NotePlayEvent(@NotNull Block block, @NotNull Instrument instrument, @NotNull Note note) {
++ this(block, instrument, note, null);
++ }
++
++ public NotePlayEvent(@NotNull Block block, @NotNull Instrument instrument, @NotNull Note note, @org.jetbrains.annotations.Nullable org.bukkit.entity.Player player) {
+ super(block);
+ this.instrument = instrument;
+ this.note = note;
++ this.player = player;
+ }
++ // DivineMC end - Add player to NotePlayEvent
+
+ @Override
+ public boolean isCancelled() {
+@@ -53,6 +_,18 @@
+ public Note getNote() {
+ return note;
+ }
++
++ // DivineMC start - Add player to NotePlayEvent
++ /**
++ * Gets the {@link org.bukkit.entity.Player} who played the note
++ *
++ * @return player who played the note, if present
++ */
++ @org.jetbrains.annotations.Nullable
++ public org.bukkit.entity.Player getPlayer() {
++ return this.player;
++ }
++ // DivineMC end - Add player to NotePlayEvent
+
+ /**
+ * Overrides the {@link Instrument} to be used.
diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch
new file mode 100644
index 0000000..beef79f
--- /dev/null
+++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch
@@ -0,0 +1,11 @@
+--- a/net/minecraft/world/entity/projectile/AbstractArrow.java
++++ b/net/minecraft/world/entity/projectile/AbstractArrow.java
+@@ -344,7 +_,7 @@
+ return this.isInGround() && this.level().noCollision(new AABB(this.position(), this.position()).inflate(0.06));
+ }
+
+- private void startFalling() {
++ public void startFalling() { // DivineMC - private -> public - AbstractArrow#startFalling
+ this.setInGround(false);
+ Vec3 deltaMovement = this.getDeltaMovement();
+ this.setDeltaMovement(deltaMovement.multiply(this.random.nextFloat() * 0.2F, this.random.nextFloat() * 0.2F, this.random.nextFloat() * 0.2F));
diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch
new file mode 100644
index 0000000..dc0218d
--- /dev/null
+++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch
@@ -0,0 +1,20 @@
+--- a/net/minecraft/world/level/block/EnderChestBlock.java
++++ b/net/minecraft/world/level/block/EnderChestBlock.java
+@@ -43,7 +_,7 @@
+ public static final EnumProperty FACING = HorizontalDirectionalBlock.FACING;
+ public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
+ protected static final VoxelShape SHAPE = Block.box(1.0, 0.0, 1.0, 15.0, 14.0, 15.0);
+- private static final Component CONTAINER_TITLE = Component.translatable("container.enderchest");
++ public static final Component CONTAINER_TITLE = Component.translatable("container.enderchest"); // DivineMC - private -> public - Open Ender Chest API
+
+ @Override
+ public MapCodec codec() {
+@@ -101,7 +_,7 @@
+ }
+
+ // Purpur start - Barrels and enderchests 6 rows
+- private ChestMenu getEnderChestSixRows(int syncId, net.minecraft.world.entity.player.Inventory inventory, Player player, PlayerEnderChestContainer playerEnderChestContainer) {
++ public static ChestMenu getEnderChestSixRows(int syncId, net.minecraft.world.entity.player.Inventory inventory, Player player, PlayerEnderChestContainer playerEnderChestContainer) { // DivineMC - private -> public static - Open Ender Chest API
+ if (org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) {
+ org.bukkit.craftbukkit.entity.CraftHumanEntity bukkitPlayer = player.getBukkitEntity();
+ if (bukkitPlayer.hasPermission("purpur.enderchest.rows.six")) {
diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch
new file mode 100644
index 0000000..de4c77a
--- /dev/null
+++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch
@@ -0,0 +1,28 @@
+--- a/net/minecraft/world/level/block/NoteBlock.java
++++ b/net/minecraft/world/level/block/NoteBlock.java
+@@ -40,6 +_,7 @@
+ public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
+ public static final IntegerProperty NOTE = BlockStateProperties.NOTE;
+ public static final int NOTE_VOLUME = 3;
++ private @Nullable Player lastPlayedBy = null; // DivineMC - Add player to NotePlayEvent
+
+ @Override
+ public MapCodec codec() {
+@@ -108,6 +_,7 @@
+
+ private void playNote(@Nullable Entity entity, BlockState state, Level level, BlockPos pos) {
+ if (level.purpurConfig.noteBlockIgnoreAbove || state.getValue(INSTRUMENT).worksAboveNoteBlock() || level.getBlockState(pos.above()).isAir()) { // Purpur - Config to allow Note Block sounds when blocked
++ if (entity instanceof Player player) this.lastPlayedBy = player; // DivineMC - Add player to NotePlayEvent
+ level.blockEvent(pos, this, 0, 0);
+ level.gameEvent(entity, GameEvent.NOTE_BLOCK_PLAY, pos);
+ }
+@@ -150,7 +_,8 @@
+ protected boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) {
+ NoteBlockInstrument noteBlockInstrument = state.getValue(INSTRUMENT);
+ // Paper start - move NotePlayEvent call to fix instrument/note changes
+- org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(level, pos, noteBlockInstrument, state.getValue(NOTE));
++ org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(level, pos, noteBlockInstrument, state.getValue(NOTE), this.lastPlayedBy); // DivineMC - Add player to NotePlayEvent
++ this.lastPlayedBy = null; // DivineMC - Add player to NotePlayEvent
+ if (event.isCancelled()) return false;
+ // Paper end - move NotePlayEvent call to fix instrument/note changes
+ float pitchFromNote;
diff --git a/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java.patch b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java.patch
new file mode 100644
index 0000000..fe38f76
--- /dev/null
+++ b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java.patch
@@ -0,0 +1,14 @@
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java
+@@ -185,4 +_,11 @@
+ this.getHandle().projectileSource = shooter;
+ }
+ // Paper end - Fix PickupStatus getting reset
++
++ // DivineMC start - Add startFalling method to AbstractArrow
++ @Override
++ public void startFalling() {
++ this.getHandle().startFalling();
++ }
++ // DivineMC end - Add startFalling method to AbstractArrow
+ }
diff --git a/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch
new file mode 100644
index 0000000..03ca598
--- /dev/null
+++ b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch
@@ -0,0 +1,22 @@
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+@@ -3640,4 +_,19 @@
+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket(getEntityId(), io.papermc.paper.adventure.PaperAdventure.asVanilla(message)));
+ }
+ // Purpur end - Death screen API
++
++ // DivineMC start - Open Ender Chest API
++ /**
++ * Opens ender chest for the player
++ *
++ * @param enderChest ender chest
++ */
++ @Override
++ public boolean openEnderChest(@NotNull org.bukkit.block.EnderChest enderChest) {
++ net.minecraft.world.inventory.PlayerEnderChestContainer playerEnderChestContainer = this.getHandle().getEnderChestInventory();
++ net.minecraft.world.level.block.entity.EnderChestBlockEntity blockEntity = ((org.bukkit.craftbukkit.block.CraftEnderChest) enderChest).getTileEntity();
++ playerEnderChestContainer.setActiveChest(blockEntity);
++ return this.getHandle().openMenu(new net.minecraft.world.SimpleMenuProvider((i, inventory, playerx) -> org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? net.minecraft.world.level.block.EnderChestBlock.getEnderChestSixRows(i, inventory, this.getHandle(), playerEnderChestContainer) : net.minecraft.world.inventory.ChestMenu.threeRows(i, inventory, playerEnderChestContainer), net.minecraft.world.level.block.EnderChestBlock.CONTAINER_TITLE)).isPresent();
++ }
++ // DivineMC end - Open Ender Chest API
+ }
diff --git a/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch
new file mode 100644
index 0000000..9c2de93
--- /dev/null
+++ b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch
@@ -0,0 +1,16 @@
+--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+@@ -1566,7 +_,12 @@
+ }
+
+ public static NotePlayEvent callNotePlayEvent(Level world, BlockPos pos, NoteBlockInstrument instrument, int note) {
+- NotePlayEvent event = new NotePlayEvent(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), org.bukkit.Instrument.getByType((byte) instrument.ordinal()), new org.bukkit.Note(note));
++ // DivineMC start - Add player to NotePlayEvent
++ return callNotePlayEvent(world, pos, instrument, note, null);
++ }
++ public static NotePlayEvent callNotePlayEvent(Level world, BlockPos pos, NoteBlockInstrument instrument, int note, @Nullable net.minecraft.world.entity.player.Player player) {
++ NotePlayEvent event = new NotePlayEvent(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), org.bukkit.Instrument.getByType((byte) instrument.ordinal()), new org.bukkit.Note(note), player instanceof ServerPlayer serverPlayer ? serverPlayer.getBukkitEntity() : null);
++ // DivineMC end - Add player to NotePlayEvent
+ world.getCraftServer().getPluginManager().callEvent(event);
+ return event;
+ }