From 358877e2e24cd0734fb53656e142ec8a345843fa Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Sun, 24 Sep 2023 17:16:58 +1000
Subject: [PATCH] Updated Upstream (Bukkit/CraftBukkit)

Upstream has released updates that appear to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
fb23cbb3 Define surefire plugin version
d022084a Define ordering for MetadataStoreTest
99a7f6f0 PR-910: Match generic max absorption attribute name style with the rest
c7390d71 PR-909: Update tests to JUnit 5

CraftBukkit Changes:
f0661c351 PR-1230: Move unstructured PDC NBT serialisation to SNBT
452fcb599 PR-1256: Update tests to JUnit 5
---
 patches/api/Add-Material-Tags.patch           | 13 ++--
 patches/api/Add-Tick-TemporalUnit.patch       |  5 +-
 patches/api/Add-missing-effects.patch         |  4 +-
 patches/api/Adventure.patch                   |  6 +-
 patches/api/Build-system-changes.patch        |  4 +-
 patches/api/Convert-project-to-Gradle.patch   | 19 ++++--
 .../api/Fix-Spigot-annotation-mistakes.patch  |  2 +-
 patches/api/Paper-Plugins.patch               | 22 +++---
 patches/api/Test-changes.patch                | 18 ++---
 ...-get-a-BlockState-without-a-snapshot.patch |  4 +-
 .../server/Add-PlayerArmorChangeEvent.patch   | 68 ++++++++-----------
 .../server/Add-StructuresLocateEvent.patch    | 24 +++----
 .../Add-methods-to-get-translation-keys.patch | 30 ++++----
 ...d-missing-structure-set-seed-configs.patch |  8 +--
 ...d-missing-team-sidebar-display-slots.patch |  4 +-
 patches/server/Add-more-advancement-API.patch |  8 +--
 ...aper-mobcaps-and-paper-playermobcaps.patch |  6 +-
 ...d-missing-default-perms-for-commands.patch | 16 ++---
 patches/server/Adventure.patch                |  4 +-
 patches/server/Bandaid-fix-for-Effect.patch   | 16 ++---
 patches/server/Build-system-changes.patch     |  4 +-
 ...on-t-call-getItemMeta-on-hasItemMeta.patch | 10 +--
 ...istake-in-CB-NBT-int-deserialization.patch |  2 +-
 ...rnColor-on-tropical-fish-bucket-meta.patch | 18 ++---
 ...Fix-silent-equipment-change-for-mobs.patch | 27 ++++----
 .../Fix-upstreams-block-state-factories.patch |  2 +-
 .../Get-entity-default-attributes.patch       | 10 +--
 patches/server/Implement-Mob-Goal-API.patch   | 27 ++++----
 .../Implement-Player-Client-Options-API.patch |  6 +-
 patches/server/Item-Rarity-API.patch          |  8 +--
 .../server/ItemStack-repair-check-API.patch   | 18 ++---
 patches/server/More-Enchantment-API.patch     | 10 +--
 .../server/More-PotionEffectType-API.patch    | 10 +--
 patches/server/Paper-Plugins.patch            | 40 +++++------
 ...y-handle-BlockBreakEvent-isDropItems.patch | 29 ++++----
 patches/server/Remap-fixes.patch              | 57 ++++++++--------
 patches/server/Setup-Gradle-project.patch     | 20 ++++--
 .../fix-item-meta-for-tadpole-buckets.patch   |  4 +-
 work/Bukkit                                   |  2 +-
 work/CraftBukkit                              |  2 +-
 40 files changed, 292 insertions(+), 295 deletions(-)

diff --git a/patches/api/Add-Material-Tags.patch b/patches/api/Add-Material-Tags.patch
index 76ece994a5..ca9d4592c5 100644
--- a/patches/api/Add-Material-Tags.patch
+++ b/patches/api/Add-Material-Tags.patch
@@ -1146,17 +1146,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +import io.papermc.paper.tag.BaseTag;
 +import io.papermc.paper.tag.EntityTags;
-+import org.bukkit.Bukkit;
-+import org.bukkit.support.AbstractTestingBase;
-+import org.junit.Test;
-+
 +import java.lang.reflect.Field;
 +import java.lang.reflect.Modifier;
 +import java.util.HashSet;
 +import java.util.Set;
 +import java.util.logging.Level;
++import org.bukkit.Bukkit;
++import org.bukkit.support.AbstractTestingBase;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertTrue;
++import static org.junit.jupiter.api.Assertions.assertTrue;
 +
 +public class MaterialTagsTest extends AbstractTestingBase {
 +
@@ -1179,7 +1178,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +    private static void testLocked(Class<?> clazz) {
 +        for (BaseTag<?, ?> tag : collectTags(clazz)) {
-+            assertTrue("Tag " + tag.key() + " is not locked", tag.isLocked());
++            assertTrue(tag.isLocked(), "Tag " + tag.key() + " is not locked");
 +        }
 +    }
 +
@@ -1209,7 +1208,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import java.util.logging.Level;
 +import org.bukkit.Bukkit;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
 +public class EntityTagsTest extends AbstractTestingBase {
 +
diff --git a/patches/api/Add-Tick-TemporalUnit.patch b/patches/api/Add-Tick-TemporalUnit.patch
index e5d3ae790d..026a2bd8f0 100644
--- a/patches/api/Add-Tick-TemporalUnit.patch
+++ b/patches/api/Add-Tick-TemporalUnit.patch
@@ -116,10 +116,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import java.time.Duration;
 +import java.time.Instant;
 +import java.time.temporal.ChronoUnit;
++import org.junit.jupiter.api.Test;
 +
-+import org.junit.Test;
-+
-+import static org.junit.Assert.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertEquals;
 +
 +public class TickTest {
 +
diff --git a/patches/api/Add-missing-effects.patch b/patches/api/Add-missing-effects.patch
index ee39d152d1..3305386976 100644
--- a/patches/api/Add-missing-effects.patch
+++ b/patches/api/Add-missing-effects.patch
@@ -212,8 +212,8 @@ diff --git a/src/test/java/org/bukkit/EffectTest.java b/src/test/java/org/bukkit
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/test/java/org/bukkit/EffectTest.java
 +++ b/src/test/java/org/bukkit/EffectTest.java
-@@ -0,0 +0,0 @@ import static org.junit.Assert.*;
- import org.junit.Test;
+@@ -0,0 +0,0 @@ import static org.hamcrest.CoreMatchers.*;
+ import org.junit.jupiter.api.Test;
  
  public class EffectTest {
 +    private static final org.apache.logging.log4j.Logger LOGGER = org.apache.logging.log4j.LogManager.getLogger(); // Paper
diff --git a/patches/api/Adventure.patch b/patches/api/Adventure.patch
index df2b50054c..3512bf3ca4 100644
--- a/patches/api/Adventure.patch
+++ b/patches/api/Adventure.patch
@@ -4992,10 +4992,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import java.util.Set;
 +import net.kyori.adventure.key.Key;
 +import org.bukkit.NamespacedKey;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertEquals;
-+import static org.junit.Assert.assertTrue;
++import static org.junit.jupiter.api.Assertions.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertTrue;
 +
 +public class KeyTest {
 +
diff --git a/patches/api/Build-system-changes.patch b/patches/api/Build-system-changes.patch
index dbe5f5e85d..58539e82d1 100644
--- a/patches/api/Build-system-changes.patch
+++ b/patches/api/Build-system-changes.patch
@@ -35,8 +35,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    // Paper end
 +
      testImplementation("org.apache.commons:commons-lang3:3.12.0")
-     testImplementation("junit:junit:4.13.2")
-     testImplementation("org.hamcrest:hamcrest-library:1.3")
+     testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
+     testImplementation("org.hamcrest:hamcrest:2.2")
 @@ -0,0 +0,0 @@ tasks.withType<Javadoc> {
      options.links(
          "https://guava.dev/releases/32.1.2-jre/api/docs/",
diff --git a/patches/api/Convert-project-to-Gradle.patch b/patches/api/Convert-project-to-Gradle.patch
index edd89049a7..5ee7d1a521 100644
--- a/patches/api/Convert-project-to-Gradle.patch
+++ b/patches/api/Convert-project-to-Gradle.patch
@@ -61,8 +61,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    testCompileOnly(annotations)
 +
 +    testImplementation("org.apache.commons:commons-lang3:3.12.0")
-+    testImplementation("junit:junit:4.13.2")
-+    testImplementation("org.hamcrest:hamcrest-library:1.3")
++    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
++    testImplementation("org.hamcrest:hamcrest:2.2")
 +    testImplementation("org.mockito:mockito-core:5.5.0")
 +    testImplementation("org.ow2.asm:asm-tree:9.5")
 +}
@@ -219,15 +219,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        </dependency>
 -        <!-- testing -->
 -        <dependency>
--            <groupId>junit</groupId>
--            <artifactId>junit</artifactId>
--            <version>4.13.2</version>
+-            <groupId>org.junit.jupiter</groupId>
+-            <artifactId>junit-jupiter</artifactId>
+-            <version>5.10.0</version>
 -            <scope>test</scope>
 -        </dependency>
 -        <dependency>
 -            <groupId>org.hamcrest</groupId>
--            <artifactId>hamcrest-library</artifactId>
--            <version>1.3</version>
+-            <artifactId>hamcrest</artifactId>
+-            <version>2.2</version>
 -            <scope>test</scope>
 -        </dependency>
 -        <dependency>
@@ -339,6 +339,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                    </tags>
 -                </configuration>
 -            </plugin>
+-            <plugin>
+-                <groupId>org.apache.maven.plugins</groupId>
+-                <artifactId>maven-surefire-plugin</artifactId>
+-                <version>3.1.0</version>
+-            </plugin>
 -        </plugins>
 -    </build>
 -
diff --git a/patches/api/Fix-Spigot-annotation-mistakes.patch b/patches/api/Fix-Spigot-annotation-mistakes.patch
index e89b82fc4d..bb63105b0a 100644
--- a/patches/api/Fix-Spigot-annotation-mistakes.patch
+++ b/patches/api/Fix-Spigot-annotation-mistakes.patch
@@ -1270,7 +1270,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +++ b/src/test/java/org/bukkit/materials/MaterialDataTest.java
 @@ -0,0 +0,0 @@ import org.bukkit.material.WoodenStep;
  import org.bukkit.material.types.MushroomBlockTexture;
- import org.junit.Test;
+ import org.junit.jupiter.api.Test;
  
 +@Deprecated // Paper
  public class MaterialDataTest {
diff --git a/patches/api/Paper-Plugins.patch b/patches/api/Paper-Plugins.patch
index 5005d5013d..144401fd46 100644
--- a/patches/api/Paper-Plugins.patch
+++ b/patches/api/Paper-Plugins.patch
@@ -2284,14 +2284,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@
 -package org.bukkit.event;
 -
+-import static org.junit.jupiter.api.Assertions.*;
 -import org.bukkit.Bukkit;
 -import org.bukkit.plugin.PluginLoader;
 -import org.bukkit.plugin.SimplePluginManager;
 -import org.bukkit.plugin.TestPlugin;
 -import org.bukkit.plugin.java.JavaPluginLoader;
 -import org.bukkit.support.AbstractTestingBase;
--import org.junit.Assert;
--import org.junit.Test;
+-import org.junit.jupiter.api.Test;
 -
 -public class SyntheticEventTest extends AbstractTestingBase {
 -    @SuppressWarnings("deprecation")
@@ -2312,7 +2312,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        pluginManager.registerEvents(impl, plugin);
 -        pluginManager.callEvent(event);
 -
--        Assert.assertEquals(1, impl.callCount);
+-        assertEquals(1, impl.callCount);
 -    }
 -
 -    public abstract static class Base<E extends Event> implements Listener {
@@ -2339,15 +2339,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@
 -package org.bukkit.plugin;
 -
+-import static org.bukkit.support.MatcherAssert.*;
 -import static org.hamcrest.Matchers.*;
--import static org.junit.Assert.*;
 -import org.bukkit.Bukkit;
 -import org.bukkit.event.Event;
 -import org.bukkit.event.TestEvent;
 -import org.bukkit.permissions.Permission;
 -import org.bukkit.support.AbstractTestingBase;
--import org.junit.After;
--import org.junit.Test;
+-import org.junit.jupiter.api.AfterEach;
+-import org.junit.jupiter.api.Test;
 -
 -public class PluginManagerTest extends AbstractTestingBase {
 -    private class MutableObject {
@@ -2503,20 +2503,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -    private void testRemovePermissionByName(final String name) {
 -        final Permission perm = new Permission(name);
 -        pm.addPermission(perm);
--        assertThat("Permission \"" + name + "\" was not added", pm.getPermission(name), is(perm));
+-        assertThat(pm.getPermission(name), is(perm), "Permission \"" + name + "\" was not added");
 -        pm.removePermission(name);
--        assertThat("Permission \"" + name + "\" was not removed", pm.getPermission(name), is(nullValue()));
+-        assertThat(pm.getPermission(name), is(nullValue()), "Permission \"" + name + "\" was not removed");
 -    }
 -
 -    private void testRemovePermissionByPermission(final String name) {
 -        final Permission perm = new Permission(name);
 -        pm.addPermission(perm);
--        assertThat("Permission \"" + name + "\" was not added", pm.getPermission(name), is(perm));
+-        assertThat(pm.getPermission(name), is(perm), "Permission \"" + name + "\" was not added");
 -        pm.removePermission(perm);
--        assertThat("Permission \"" + name + "\" was not removed", pm.getPermission(name), is(nullValue()));
+-        assertThat(pm.getPermission(name), is(nullValue()), "Permission \"" + name + "\" was not removed");
 -    }
 -
--    @After
+-    @AfterEach
 -    public void tearDown() {
 -        pm.clearPlugins();
 -        assertThat(pm.getPermissions(), is(empty()));
diff --git a/patches/api/Test-changes.patch b/patches/api/Test-changes.patch
index 9e1db3548c..bef542d982 100644
--- a/patches/api/Test-changes.patch
+++ b/patches/api/Test-changes.patch
@@ -146,8 +146,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            builder.append("\t").append(message).append("\n");
          }
  
--        Assert.fail("There " + errors.size() + " are missing annotation(s)");
-+        Assert.fail(builder.toString());
+-        fail("There " + errors.size() + " are missing annotation(s)");
++        fail(builder.toString());
      }
  
      private static void collectClasses(@NotNull File from, @NotNull Map<String, ClassNode> to) throws IOException {
@@ -208,14 +208,14 @@ diff --git a/src/test/java/org/bukkit/BukkitMirrorTest.java b/src/test/java/org/
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/test/java/org/bukkit/BukkitMirrorTest.java
 +++ b/src/test/java/org/bukkit/BukkitMirrorTest.java
-@@ -0,0 +0,0 @@ public class BukkitMirrorTest {
+@@ -0,0 +0,0 @@ import org.junit.jupiter.params.provider.MethodSource;
+ public class BukkitMirrorTest {
  
-     @Parameters(name = "{index}: {1}")
-     public static List<Object[]> data() {
-+        if (true) return List.of(); // Paper
-         return Lists.transform(Arrays.asList(Server.class.getDeclaredMethods()), new Function<Method, Object[]>() {
-             @Override
-             public Object[] apply(Method input) {
+     public static Stream<Arguments> data() {
++        if (true) return Stream.of(); // Paper
+         return Stream.of(Server.class.getDeclaredMethods())
+                 .map(method -> {
+                     try {
 diff --git a/src/test/java/org/bukkit/support/TestServer.java b/src/test/java/org/bukkit/support/TestServer.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/test/java/org/bukkit/support/TestServer.java
diff --git a/patches/server/API-to-get-a-BlockState-without-a-snapshot.patch b/patches/server/API-to-get-a-BlockState-without-a-snapshot.patch
index f6211c960e..97716b7760 100644
--- a/patches/server/API-to-get-a-BlockState-without-a-snapshot.patch
+++ b/patches/server/API-to-get-a-BlockState-without-a-snapshot.patch
@@ -153,8 +153,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
 +++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
 @@ -0,0 +0,0 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
-     public Map<String, Object> serialize() {
-         return (Map<String, Object>) CraftNBTTagConfigSerializer.serialize(this.toTagCompound());
+     public String serialize() {
+         return CraftNBTTagConfigSerializer.serialize(this.toTagCompound());
      }
 +
 +    // Paper start
diff --git a/patches/server/Add-PlayerArmorChangeEvent.patch b/patches/server/Add-PlayerArmorChangeEvent.patch
index 71214f825b..22f3a9c6c7 100644
--- a/patches/server/Add-PlayerArmorChangeEvent.patch
+++ b/patches/server/Add-PlayerArmorChangeEvent.patch
@@ -40,6 +40,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent;
 +import java.util.ArrayList;
 +import java.util.List;
++import java.util.stream.Stream;
 +import net.minecraft.world.entity.EquipmentSlot;
 +import net.minecraft.world.item.Equipable;
 +import net.minecraft.world.item.Item;
@@ -47,45 +48,38 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.Material;
 +import org.bukkit.craftbukkit.util.CraftMagicNumbers;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.Test;
-+import org.junit.runner.RunWith;
-+import org.junit.runners.Parameterized;
++import org.junit.jupiter.params.ParameterizedTest;
++import org.junit.jupiter.params.provider.MethodSource;
 +
-+import static org.junit.Assert.assertEquals;
-+import static org.junit.Assert.assertNotNull;
++import static org.junit.jupiter.api.Assertions.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertNotNull;
 +
-+@RunWith(Parameterized.class)
 +public class ExtraArmorSlotTypeMaterialTest extends AbstractTestingBase {
 +
-+    @Parameterized.Parameter(0)
-+    public PlayerArmorChangeEvent.SlotType slotType;
-+
-+    @Parameterized.Parameter(1)
-+    public Material item;
-+
-+    @Parameterized.Parameters(name = "{0}: {1}")
-+    public static Iterable<Object[]> parameters() {
++    @MethodSource("parameters")
++    public static Stream<Object[]> parameters() {
 +        final List<Object[]> parameters = new ArrayList<>();
 +        for (final PlayerArmorChangeEvent.SlotType slotType : PlayerArmorChangeEvent.SlotType.values()) {
 +            for (final Material item : slotType.getTypes()) {
 +                parameters.add(new Object[]{ slotType, item });
 +            }
 +        }
-+        return parameters;
++        return parameters.stream();
 +    }
 +
-+    @Test
-+    public void test() {
-+        final Item nmsItem = CraftMagicNumbers.getItem(this.item);
++    @ParameterizedTest
++    @MethodSource("parameters")
++    public void test(PlayerArmorChangeEvent.SlotType slotType, Material item) {
++        final Item nmsItem = CraftMagicNumbers.getItem(item);
 +        final Equipable equipable = Equipable.get(new ItemStack(nmsItem));
-+        assertNotNull(this.item + " isn't equipable", equipable);
-+        final EquipmentSlot slot = switch (this.slotType) {
++        assertNotNull(equipable, item + " isn't equipable");
++        final EquipmentSlot slot = switch (slotType) {
 +            case HEAD -> EquipmentSlot.HEAD;
 +            case CHEST -> EquipmentSlot.CHEST;
 +            case LEGS -> EquipmentSlot.LEGS;
 +            case FEET -> EquipmentSlot.FEET;
 +        };
-+        assertEquals(this.item + " isn't set to the right slot", equipable.getEquipmentSlot(), slot);
++        assertEquals(equipable.getEquipmentSlot(), slot, item + " isn't set to the right slot");
 +    }
 +}
 diff --git a/src/test/java/io/papermc/paper/inventory/item/MissingArmorSlotTypeMaterialTest.java b/src/test/java/io/papermc/paper/inventory/item/MissingArmorSlotTypeMaterialTest.java
@@ -99,6 +93,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent;
 +import java.util.ArrayList;
 +import java.util.List;
++import java.util.stream.Stream;
 +import net.minecraft.core.registries.BuiltInRegistries;
 +import net.minecraft.world.entity.EquipmentSlot;
 +import net.minecraft.world.item.Equipable;
@@ -106,39 +101,32 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import net.minecraft.world.item.ItemStack;
 +import org.bukkit.craftbukkit.util.CraftMagicNumbers;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.Test;
-+import org.junit.runner.RunWith;
-+import org.junit.runners.Parameterized;
++import org.junit.jupiter.params.ParameterizedTest;
++import org.junit.jupiter.params.provider.MethodSource;
 +
-+import static org.junit.Assert.assertTrue;
++import static org.junit.jupiter.api.Assertions.assertTrue;
 +
 +/**
 + * Test for {@link com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType}
 + */
-+@RunWith(Parameterized.class)
 +public class MissingArmorSlotTypeMaterialTest extends AbstractTestingBase {
 +
-+    @Parameterized.Parameter(0)
-+    public Equipable equipable;
-+
-+    @Parameterized.Parameter(1)
-+    public Item item;
-+
-+    @Parameterized.Parameters(name = "{1}")
-+    public static Iterable<Object[]> parameters() {
++    @MethodSource("parameters")
++    public static Stream<Object[]> parameters() {
 +        final List<Object[]> parameters = new ArrayList<>();
 +        for (final Item item : BuiltInRegistries.ITEM) {
 +            final Equipable equipable = Equipable.get(new ItemStack(item));
 +            if (equipable != null) {
-+                parameters.add(new Object[]{ equipable, item });
++                parameters.add(new Object[]{equipable, item});
 +            }
 +        }
-+        return parameters;
++        return parameters.stream();
 +    }
 +
-+    @Test
-+    public void test() {
-+        final EquipmentSlot equipmentSlot = this.equipable.getEquipmentSlot();
++    @ParameterizedTest
++    @MethodSource("parameters")
++    public void test(Equipable equipable, Item item) {
++        final EquipmentSlot equipmentSlot = equipable.getEquipmentSlot();
 +        PlayerArmorChangeEvent.SlotType slotType = switch (equipmentSlot) {
 +            case HEAD -> PlayerArmorChangeEvent.SlotType.HEAD;
 +            case CHEST -> PlayerArmorChangeEvent.SlotType.CHEST;
@@ -147,7 +135,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            default -> null;
 +        };
 +        if (slotType != null) {
-+            assertTrue("SlotType " + slotType + " doesn't include " + this.item, slotType.getTypes().contains(CraftMagicNumbers.getMaterial(this.item)));
++            assertTrue(slotType.getTypes().contains(CraftMagicNumbers.getMaterial(item)), "SlotType " + slotType + " doesn't include " + item);
 +        }
 +    }
 +}
diff --git a/patches/server/Add-StructuresLocateEvent.patch b/patches/server/Add-StructuresLocateEvent.patch
index bb6c012045..d379cbfb76 100644
--- a/patches/server/Add-StructuresLocateEvent.patch
+++ b/patches/server/Add-StructuresLocateEvent.patch
@@ -110,9 +110,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.NamespacedKey;
 +import org.bukkit.craftbukkit.util.CraftNamespacedKey;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.AfterClass;
-+import org.junit.BeforeClass;
-+import org.junit.Test;
++import org.junit.jupiter.api.AfterAll;
++import org.junit.jupiter.api.BeforeAll;
++import org.junit.jupiter.api.Test;
 +
 +import java.io.PrintStream;
 +import java.lang.reflect.Field;
@@ -121,9 +121,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import java.util.Map;
 +import java.util.StringJoiner;
 +
-+import static org.junit.Assert.assertEquals;
-+import static org.junit.Assert.assertNotNull;
-+import static org.junit.Assert.assertTrue;
++import static org.junit.jupiter.api.Assertions.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertNotNull;
++import static org.junit.jupiter.api.Assertions.assertTrue;
 +
 +@Deprecated(forRemoval = true)
 +public class ConfiguredStructureTest extends AbstractTestingBase {
@@ -133,7 +133,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +    private static PrintStream out;
 +
-+    @BeforeClass
++    @BeforeAll
 +    public static void collectStructures() throws ReflectiveOperationException {
 +        out = System.out;
 +        System.setOut(Bootstrap.STDOUT);
@@ -153,12 +153,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    @Test
 +    public void testMinecraftToApi() {
 +        Registry<Structure> structureRegistry = AbstractTestingBase.REGISTRY_CUSTOM.registryOrThrow(Registries.STRUCTURE);
-+        assertEquals("configured structure maps should be the same size", BUILT_IN_STRUCTURES.size(), structureRegistry.size());
++        assertEquals(BUILT_IN_STRUCTURES.size(), structureRegistry.size(), "configured structure maps should be the same size");
 +
 +        Map<ResourceLocation, Structure> missing = new LinkedHashMap<>();
 +        for (Structure feature : structureRegistry) {
 +            final ResourceLocation key = structureRegistry.getKey(feature);
-+            assertNotNull("Missing built-in registry key", key);
++            assertNotNull(key, "Missing built-in registry key");
 +            if (key.equals(BuiltinStructures.ANCIENT_CITY.location()) || key.equals(BuiltinStructures.TRAIL_RUINS.location())) {
 +                continue; // TODO remove when upstream adds "jigsaw" StructureType
 +            }
@@ -167,14 +167,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            }
 +        }
 +
-+        assertTrue(printMissing(missing), missing.isEmpty());
++        assertTrue(missing.isEmpty(), printMissing(missing));
 +    }
 +
 +    @Test
 +    public void testApiToMinecraft() {
 +        Registry<Structure> structureRegistry = AbstractTestingBase.REGISTRY_CUSTOM.registryOrThrow(Registries.STRUCTURE);
 +        for (NamespacedKey apiKey : DEFAULT_CONFIGURED_STRUCTURES.keySet()) {
-+            assertTrue(apiKey + " does not have a minecraft counterpart", structureRegistry.containsKey(CraftNamespacedKey.toMinecraft(apiKey)));
++            assertTrue(structureRegistry.containsKey(CraftNamespacedKey.toMinecraft(apiKey)), apiKey + " does not have a minecraft counterpart");
 +        }
 +    }
 +
@@ -188,7 +188,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        return joiner.toString();
 +    }
 +
-+    @AfterClass
++    @AfterAll
 +    public static void after() {
 +        System.setOut(out);
 +    }
diff --git a/patches/server/Add-methods-to-get-translation-keys.patch b/patches/server/Add-methods-to-get-translation-keys.patch
index 565751466b..38fb028ab0 100644
--- a/patches/server/Add-methods-to-get-translation-keys.patch
+++ b/patches/server/Add-methods-to-get-translation-keys.patch
@@ -64,9 +64,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.attribute.Attribute;
 +import org.bukkit.craftbukkit.util.CraftNamespacedKey;
 +import org.bukkit.support.AbstractTestingBase;
- import org.junit.Assert;
-+import org.junit.Ignore;
- import org.junit.Test;
+ import org.junit.jupiter.api.Assertions;
++import org.junit.jupiter.api.Disabled;
+ import org.junit.jupiter.api.Test;
  
 -public class TranslationKeyTest {
 +public class TranslationKeyTest extends AbstractTestingBase {
@@ -74,44 +74,44 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      @Test
      public void testChatVisibilityKeys() {
 @@ -0,0 +0,0 @@ public class TranslationKeyTest {
-             Assert.assertEquals(chatVisibility + "'s translation key doesn't match", ChatVisiblity.valueOf(chatVisibility.name()).getKey(), chatVisibility.translationKey());
+             Assertions.assertEquals(chatVisibility + "'s translation key doesn't match", ChatVisiblity.valueOf(chatVisibility.name()).getKey(), chatVisibility.translationKey());
          }
      }
 +
 +    @Test
 +    public void testDifficultyKeys() {
 +        for (Difficulty bukkitDifficulty : Difficulty.values()) {
-+            Assert.assertEquals(bukkitDifficulty + "'s translation key doesn't match", ((TranslatableContents) net.minecraft.world.Difficulty.byId(bukkitDifficulty.ordinal()).getDisplayName().getContents()).getKey(), bukkitDifficulty.translationKey());
++            Assertions.assertEquals(bukkitDifficulty + "'s translation key doesn't match", ((TranslatableContents) net.minecraft.world.Difficulty.byId(bukkitDifficulty.ordinal()).getDisplayName().getContents()).getKey(), bukkitDifficulty.translationKey());
 +        }
 +    }
 +
 +    @Test
 +    public void testGameruleKeys() {
 +        for (GameRule<?> rule : GameRule.values()) {
-+            Assert.assertEquals(rule.getName() + "'s translation doesn't match", org.bukkit.craftbukkit.CraftWorld.getGameRulesNMS().get(rule.getName()).getDescriptionId(), rule.translationKey());
++            Assertions.assertEquals(rule.getName() + "'s translation doesn't match", org.bukkit.craftbukkit.CraftWorld.getGameRulesNMS().get(rule.getName()).getDescriptionId(), rule.translationKey());
 +        }
 +    }
 +
 +    @Test
 +    public void testAttributeKeys() {
 +        for (Attribute attribute : Attribute.values()) {
-+            Assert.assertEquals("translation key mismatch for " + attribute, org.bukkit.craftbukkit.attribute.CraftAttribute.bukkitToMinecraft(attribute).getDescriptionId(), attribute.translationKey());
++            Assertions.assertEquals("translation key mismatch for " + attribute, org.bukkit.craftbukkit.attribute.CraftAttribute.bukkitToMinecraft(attribute).getDescriptionId(), attribute.translationKey());
 +        }
 +    }
 +
 +    @Test
 +    public void testFireworkEffectType() {
 +        for (FireworkEffect.Type type : FireworkEffect.Type.values()) {
-+            Assert.assertEquals("translation key mismatch for " + type, net.minecraft.world.item.FireworkRocketItem.Shape.byId(org.bukkit.craftbukkit.inventory.CraftMetaFirework.getNBT(type)).getName(), org.bukkit.FireworkEffect.Type.NAMES.key(type));
++            Assertions.assertEquals("translation key mismatch for " + type, net.minecraft.world.item.FireworkRocketItem.Shape.byId(org.bukkit.craftbukkit.inventory.CraftMetaFirework.getNBT(type)).getName(), org.bukkit.FireworkEffect.Type.NAMES.key(type));
 +        }
 +    }
 +
 +    @Test
-+    @Ignore // TODO fix
++    @Disabled // TODO fix
 +    public void testCreativeCategory() {
 +        // for (CreativeModeTab tab : CreativeModeTabs.tabs()) {
 +        //     CreativeCategory category = Objects.requireNonNull(CraftCreativeCategory.fromNMS(tab));
-+        //     Assert.assertEquals("translation key mismatch for " + category, ((TranslatableContents) tab.getDisplayName().getContents()).getKey(), category.translationKey());
++        //     Assertions.assertEquals("translation key mismatch for " + category, ((TranslatableContents) tab.getDisplayName().getContents()).getKey(), category.translationKey());
 +        // }
 +    }
 +
@@ -119,8 +119,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public void testGameMode() {
 +        for (GameType nms : GameType.values()) {
 +            GameMode bukkit = GameMode.getByValue(nms.getId());
-+            Assert.assertNotNull(bukkit);
-+            Assert.assertEquals("translation key mismatch for " + bukkit, ((TranslatableContents) nms.getLongDisplayName().getContents()).getKey(), bukkit.translationKey());
++            Assertions.assertNotNull(bukkit);
++            Assertions.assertEquals("translation key mismatch for " + bukkit, ((TranslatableContents) nms.getLongDisplayName().getContents()).getKey(), bukkit.translationKey());
 +        }
 +    }
 +
@@ -128,7 +128,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public void testBiome() {
 +        for (Map.Entry<ResourceKey<Biome>, Biome> nms : AbstractTestingBase.BIOMES.entrySet()) {
 +            org.bukkit.block.Biome bukkit = org.bukkit.block.Biome.valueOf(nms.getKey().location().getPath().toUpperCase());
-+            Assert.assertEquals("translation key mismatch for " + bukkit, nms.getKey().location().toLanguageKey("biome"), bukkit.translationKey());
++            Assertions.assertEquals("translation key mismatch for " + bukkit, nms.getKey().location().toLanguageKey("biome"), bukkit.translationKey());
 +        }
 +    }
 +
@@ -136,8 +136,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public void testMusicInstrument() {
 +        for (final ResourceLocation nms : BuiltInRegistries.INSTRUMENT.keySet()) {
 +            final MusicInstrument bukkit = MusicInstrument.getByKey(CraftNamespacedKey.fromMinecraft(nms));
-+            Assert.assertNotNull("Missing bukkit instrument for " + nms, bukkit);
-+            Assert.assertEquals("translation key mismatch for " + bukkit, nms.toLanguageKey("instrument"), bukkit.translationKey());
++            Assertions.assertNotNull(bukkit, "Missing bukkit instrument for " + nms);
++            Assertions.assertEquals("translation key mismatch for " + bukkit, nms.toLanguageKey("instrument"), bukkit.translationKey());
 +        }
 +    }
  }
diff --git a/patches/server/Add-missing-structure-set-seed-configs.patch b/patches/server/Add-missing-structure-set-seed-configs.patch
index 9fae4091c2..04517c6500 100644
--- a/patches/server/Add-missing-structure-set-seed-configs.patch
+++ b/patches/server/Add-missing-structure-set-seed-configs.patch
@@ -288,11 +288,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.configuration.file.YamlConfiguration;
 +import org.bukkit.support.AbstractTestingBase;
 +import org.jetbrains.annotations.NotNull;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +import org.spigotmc.SpigotConfig;
 +import org.spigotmc.SpigotWorldConfig;
 +
-+import static org.junit.Assert.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertEquals;
 +
 +public class StructureSeedConfigTest extends AbstractTestingBase {
 +
@@ -340,10 +340,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                final Field field = StructurePlacement.class.getDeclaredField("HIGHLY_ARBITRARY_RANDOM_SALT");
 +                field.trySetAccessible();
 +                assertEquals(0, set.placement().salt);
-+                assertEquals("Mismatched default seed for " + setKey + ". Should be " + field.get(null), field.get(null), salt);
++                assertEquals(field.get(null), salt, "Mismatched default seed for " + setKey + ". Should be " + field.get(null));
 +                continue;
 +            }
-+            assertEquals("Mismatched default seed for " + setKey + ". Should be " + set.placement().salt, set.placement().salt, salt);
++            assertEquals(set.placement().salt, salt, "Mismatched default seed for " + setKey + ". Should be " + set.placement().salt);
 +        }
 +    }
 +}
diff --git a/patches/server/Add-missing-team-sidebar-display-slots.patch b/patches/server/Add-missing-team-sidebar-display-slots.patch
index 1a6b8c6a95..fc42310d39 100644
--- a/patches/server/Add-missing-team-sidebar-display-slots.patch
+++ b/patches/server/Add-missing-team-sidebar-display-slots.patch
@@ -80,9 +80,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +import org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations;
 +import org.bukkit.scoreboard.DisplaySlot;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertNotNull;
++import static org.junit.jupiter.api.Assertions.assertNotNull;
 +
 +public class DisplaySlotTest {
 +
diff --git a/patches/server/Add-more-advancement-API.patch b/patches/server/Add-more-advancement-API.patch
index a442fa71ef..875a60fac0 100644
--- a/patches/server/Add-more-advancement-API.patch
+++ b/patches/server/Add-more-advancement-API.patch
@@ -199,9 +199,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import net.kyori.adventure.text.format.TextColor;
 +import net.minecraft.advancements.FrameType;
 +import net.minecraft.network.chat.contents.TranslatableContents;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertEquals;
 +
 +public class AdvancementFrameTest {
 +
@@ -211,8 +211,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            final TextColor expectedColor = PaperAdventure.asAdventure(nmsFrameType.getChatColor());
 +            final String expectedTranslationKey = ((TranslatableContents) nmsFrameType.getDisplayName().getContents()).getKey();
 +            final var frame = PaperAdvancementDisplay.asPaperFrame(nmsFrameType);
-+            assertEquals("The translation keys should be the same", expectedTranslationKey, frame.translationKey());
-+            assertEquals("The frame colors should be the same", expectedColor, frame.color());
++            assertEquals(expectedTranslationKey, frame.translationKey(), "The translation keys should be the same");
++            assertEquals(expectedColor, frame.color(), "The frame colors should be the same");
 +            assertEquals(nmsFrameType.getName(), AdvancementDisplay.Frame.NAMES.key(frame));
 +        }
 +    }
diff --git a/patches/server/Add-paper-mobcaps-and-paper-playermobcaps.patch b/patches/server/Add-paper-mobcaps-and-paper-playermobcaps.patch
index d3446f38ad..dba30578d1 100644
--- a/patches/server/Add-paper-mobcaps-and-paper-playermobcaps.patch
+++ b/patches/server/Add-paper-mobcaps-and-paper-playermobcaps.patch
@@ -324,8 +324,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import java.util.HashSet;
 +import java.util.Set;
 +import net.minecraft.world.entity.MobCategory;
-+import org.junit.Assert;
-+import org.junit.Test;
++import org.junit.jupiter.api.Assertions;
++import org.junit.jupiter.api.Test;
 +
 +public class MobcapsCommandTest {
 +    @Test
@@ -336,6 +336,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                missing.add(value.getName());
 +            }
 +        }
-+        Assert.assertTrue("MobcapsCommand.MOB_CATEGORY_COLORS map missing TextColors for [" + String.join(", ", missing + "]"), missing.isEmpty());
++        Assertions.assertTrue(missing.isEmpty(), "MobcapsCommand.MOB_CATEGORY_COLORS map missing TextColors for [" + String.join(", ", missing + "]"));
 +    }
 +}
diff --git a/patches/server/Added-missing-default-perms-for-commands.patch b/patches/server/Added-missing-default-perms-for-commands.patch
index 7926bcf618..033d7958d0 100644
--- a/patches/server/Added-missing-default-perms-for-commands.patch
+++ b/patches/server/Added-missing-default-perms-for-commands.patch
@@ -104,9 +104,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions;
 +import org.bukkit.permissions.Permission;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.AfterClass;
-+import org.junit.BeforeClass;
-+import org.junit.Test;
++import org.junit.jupiter.api.AfterAll;
++import org.junit.jupiter.api.BeforeAll;
++import org.junit.jupiter.api.Test;
 +
 +import java.io.PrintStream;
 +import java.util.HashSet;
@@ -115,12 +115,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import java.util.Set;
 +import java.util.TreeSet;
 +
-+import static org.junit.Assert.assertTrue;
++import static org.junit.jupiter.api.Assertions.assertTrue;
 +
 +public class MinecraftCommandPermissionsTest extends AbstractTestingBase {
 +
 +    private static PrintStream old;
-+    @BeforeClass
++    @BeforeAll
 +    public static void before() {
 +        old = System.out;
 +        System.setOut(Bootstrap.STDOUT);
@@ -143,9 +143,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                foundPerms.add(vanillaPerm);
 +            }
 +        }
-+        assertTrue("Commands missing permissions: \n" + String.join("\n", missing), missing.isEmpty());
++        assertTrue(missing.isEmpty(), "Commands missing permissions: \n" + String.join("\n", missing));
 +        perms.removeAll(foundPerms);
-+        assertTrue("Extra permissions not associated with a command: \n" + String.join("\n", perms), perms.isEmpty());
++        assertTrue(perms.isEmpty(), "Extra permissions not associated with a command: \n" + String.join("\n", perms));
 +    }
 +
 +    private static final List<String> TO_SKIP = List.of(
@@ -165,7 +165,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        return perms;
 +    }
 +
-+    @AfterClass
++    @AfterAll
 +    public static void after() {
 +        if (old != null) {
 +            System.setOut(old);
diff --git a/patches/server/Adventure.patch b/patches/server/Adventure.patch
index 3e8d62408b..f7873b1077 100644
--- a/patches/server/Adventure.patch
+++ b/patches/server/Adventure.patch
@@ -5070,9 +5070,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import net.kyori.adventure.text.format.NamedTextColor;
 +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
 +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertEquals;
 +
 +public class ComponentServicesTest {
 +
diff --git a/patches/server/Bandaid-fix-for-Effect.patch b/patches/server/Bandaid-fix-for-Effect.patch
index 6a6da00421..37a97b8e48 100644
--- a/patches/server/Bandaid-fix-for-Effect.patch
+++ b/patches/server/Bandaid-fix-for-Effect.patch
@@ -85,11 +85,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import java.util.List;
 +import java.util.Map;
 +import net.minecraft.world.level.block.LevelEvent;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertNotNull;
-+import static org.junit.Assert.assertNull;
-+import static org.junit.Assert.assertTrue;
++import static org.junit.jupiter.api.Assertions.assertNotNull;
++import static org.junit.jupiter.api.Assertions.assertNull;
++import static org.junit.jupiter.api.Assertions.assertTrue;
 +
 +public class EffectTest {
 +
@@ -114,12 +114,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        for (final Effect effect : Effect.values()) {
 +            if (isNotDeprecated(effect)) {
 +                final Effect put = toId.put(effect.getId(), effect);
-+                assertNull("duplicate API effect: " + put, put);
++                assertNull(put, "duplicate API effect: " + put);
 +            }
 +        }
 +
 +        for (final Integer event : collectNmsLevelEvents()) {
-+            assertNotNull("missing API Effect: " + event, toId.get(event));
++            assertNotNull(toId.get(event), "missing API Effect: " + event);
 +        }
 +    }
 +
@@ -130,13 +130,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        for (final Effect effect : Effect.values()) {
 +            if (isNotDeprecated(effect)) {
 +                final Effect put = toId.put(effect.getId(), effect);
-+                assertNull("duplicate API effect: " + put, put);
++                assertNull(put, "duplicate API effect: " + put);
 +            }
 +        }
 +
 +        final List<Integer> nmsEvents = collectNmsLevelEvents();
 +        for (final Map.Entry<Integer, Effect> entry : toId.entrySet()) {
-+            assertTrue("Extra API Effect: " + entry.getValue(), nmsEvents.contains(entry.getKey()));
++            assertTrue(nmsEvents.contains(entry.getKey()), "Extra API Effect: " + entry.getValue());
 +        }
 +    }
 +}
diff --git a/patches/server/Build-system-changes.patch b/patches/server/Build-system-changes.patch
index 87f54e4f94..141f76dbf1 100644
--- a/patches/server/Build-system-changes.patch
+++ b/patches/server/Build-system-changes.patch
@@ -27,8 +27,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      runtimeOnly("com.mysql:mysql-connector-j:8.1.0")
 @@ -0,0 +0,0 @@ dependencies {
  
-     testImplementation("junit:junit:4.13.2")
-     testImplementation("org.hamcrest:hamcrest-library:1.3")
+     testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
+     testImplementation("org.hamcrest:hamcrest:2.2")
 +
 +    implementation("io.netty:netty-all:4.1.87.Final"); // Paper - Bump netty
  }
diff --git a/patches/server/Don-t-call-getItemMeta-on-hasItemMeta.patch b/patches/server/Don-t-call-getItemMeta-on-hasItemMeta.patch
index 9b5e953099..67932e8113 100644
--- a/patches/server/Don-t-call-getItemMeta-on-hasItemMeta.patch
+++ b/patches/server/Don-t-call-getItemMeta-on-hasItemMeta.patch
@@ -33,19 +33,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 +    // Paper start
 +    private void testItemMeta(ItemStack stack) {
-+        assertThat("Should not have ItemMeta", stack.hasItemMeta(), is(false));
++        assertThat(stack.hasItemMeta(), is(false), "Should not have ItemMeta");
 +
 +        stack.setDurability((short) 0);
-+        assertThat("ItemStack with zero durability should not have ItemMeta", stack.hasItemMeta(), is(false));
++        assertThat(stack.hasItemMeta(), is(false), "ItemStack with zero durability should not have ItemMeta");
 +
 +        stack.setDurability((short) 2);
-+        assertThat("ItemStack with non-zero durability should have ItemMeta", stack.hasItemMeta(), is(true));
++        assertThat(stack.hasItemMeta(), is(true), "ItemStack with non-zero durability should have ItemMeta");
 +
 +        stack.setLore(java.util.Collections.singletonList("Lore"));
-+        assertThat("ItemStack with lore and durability should have ItemMeta", stack.hasItemMeta(), is(true));
++        assertThat(stack.hasItemMeta(), is(true), "ItemStack with lore and durability should have ItemMeta");
 +
 +        stack.setDurability((short) 0);
-+        assertThat("ItemStack with lore should have ItemMeta", stack.hasItemMeta(), is(true));
++        assertThat(stack.hasItemMeta(), is(true), "ItemStack with lore should have ItemMeta");
 +
 +        stack.setLore(null);
 +    }
diff --git a/patches/server/Fix-regex-mistake-in-CB-NBT-int-deserialization.patch b/patches/server/Fix-regex-mistake-in-CB-NBT-int-deserialization.patch
index 2be4ea33cd..c1023aba3c 100644
--- a/patches/server/Fix-regex-mistake-in-CB-NBT-int-deserialization.patch
+++ b/patches/server/Fix-regex-mistake-in-CB-NBT-int-deserialization.patch
@@ -16,7 +16,7 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftNBTTagConfigSerializ
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftNBTTagConfigSerializer.java
 +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftNBTTagConfigSerializer.java
-@@ -0,0 +0,0 @@ import net.minecraft.nbt.TagParser;
+@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
  public class CraftNBTTagConfigSerializer {
  
      private static final Pattern ARRAY = Pattern.compile("^\\[.*]");
diff --git a/patches/server/Fix-setPatternColor-on-tropical-fish-bucket-meta.patch b/patches/server/Fix-setPatternColor-on-tropical-fish-bucket-meta.patch
index a0d4cc8668..667abdfb02 100644
--- a/patches/server/Fix-setPatternColor-on-tropical-fish-bucket-meta.patch
+++ b/patches/server/Fix-setPatternColor-on-tropical-fish-bucket-meta.patch
@@ -37,15 +37,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.inventory.ItemStack;
 +import org.bukkit.inventory.meta.TropicalFishBucketMeta;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.Assert;
-+import org.junit.Test;
++import org.junit.jupiter.api.Assertions;
++import org.junit.jupiter.api.Test;
 +
 +public class CraftMetaTropicalFishBucketTest extends AbstractTestingBase {
 +
 +    @Test
 +    public void testAllCombinations() {
 +        final var rawMeta = new ItemStack(Material.TROPICAL_FISH_BUCKET).getItemMeta();
-+        Assert.assertTrue("Meta was not a tropical fish bucket", rawMeta instanceof TropicalFishBucketMeta);
++        Assertions.assertTrue(rawMeta instanceof TropicalFishBucketMeta, "Meta was not a tropical fish bucket");
 +
 +        final var meta = (TropicalFishBucketMeta) rawMeta;
 +
@@ -53,16 +53,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            for (final var pattern : TropicalFish.Pattern.values()) {
 +                for (final var patternColor : DyeColor.values()) {
 +                    meta.setBodyColor(bodyColor);
-+                    Assert.assertEquals("Body color did not match post body color!", bodyColor, meta.getBodyColor());
++                    Assertions.assertEquals(bodyColor, meta.getBodyColor(), "Body color did not match post body color!");
 +
 +                    meta.setPattern(pattern);
-+                    Assert.assertEquals("Pattern did not match post pattern!", pattern, meta.getPattern());
-+                    Assert.assertEquals("Body color did not match post pattern!", bodyColor, meta.getBodyColor());
++                    Assertions.assertEquals(pattern, meta.getPattern(), "Pattern did not match post pattern!");
++                    Assertions.assertEquals(bodyColor, meta.getBodyColor(), "Body color did not match post pattern!");
 +
 +                    meta.setPatternColor(patternColor);
-+                    Assert.assertEquals("Pattern did not match post pattern color!", pattern, meta.getPattern());
-+                    Assert.assertEquals("Body color did not match post pattern color!", bodyColor, meta.getBodyColor());
-+                    Assert.assertEquals("Pattern color did not match post pattern color!", patternColor, meta.getPatternColor());
++                    Assertions.assertEquals(pattern, meta.getPattern(), "Pattern did not match post pattern color!");
++                    Assertions.assertEquals(bodyColor, meta.getBodyColor(), "Body color did not match post pattern color!");
++                    Assertions.assertEquals(patternColor, meta.getPatternColor(), "Pattern color did not match post pattern color!");
 +                }
 +            }
 +        }
diff --git a/patches/server/Fix-silent-equipment-change-for-mobs.patch b/patches/server/Fix-silent-equipment-change-for-mobs.patch
index 9ffcfc332e..835510c9af 100644
--- a/patches/server/Fix-silent-equipment-change-for-mobs.patch
+++ b/patches/server/Fix-silent-equipment-change-for-mobs.patch
@@ -78,21 +78,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import io.github.classgraph.ScanResult;
 +import java.util.ArrayList;
 +import java.util.List;
++import java.util.stream.Stream;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.Test;
-+import org.junit.runner.RunWith;
-+import org.junit.runners.Parameterized;
++import org.junit.jupiter.params.ParameterizedTest;
++import org.junit.jupiter.params.provider.MethodSource;
 +
-+import static org.junit.Assert.fail;
++import static org.junit.jupiter.api.Assertions.fail;
 +
-+@RunWith(Parameterized.class)
 +public class EntitySetItemSlotSilentOverrideTest extends AbstractTestingBase {
 +
-+    @Parameterized.Parameter
-+    public ClassInfo overridesSetItemSlot;
-+
-+    @Parameterized.Parameters(name = "{0}")
-+    public static Iterable<ClassInfo> parameters() {
++    @MethodSource("parameters")
++    public static Stream<ClassInfo> parameters() {
 +        final List<ClassInfo> classInfo = new ArrayList<>();
 +        try (ScanResult scanResult = new ClassGraph()
 +            .enableClassInfo()
@@ -107,12 +103,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                }
 +            }
 +        }
-+        return classInfo;
++        return classInfo.stream();
 +    }
 +
-+    @Test
-+    public void checkSetItemSlotSilentOverrides() {
-+        final MethodInfoList setItemSlot = this.overridesSetItemSlot.getDeclaredMethodInfo("setItemSlot");
++    @ParameterizedTest
++    @MethodSource("parameters")
++    public void checkSetItemSlotSilentOverrides(ClassInfo overridesSetItemSlot) {
++        final MethodInfoList setItemSlot = overridesSetItemSlot.getDeclaredMethodInfo("setItemSlot");
 +        for (final MethodInfo methodInfo : setItemSlot) {
 +            for (final MethodParameterInfo methodParameterInfo : methodInfo.getParameterInfo()) {
 +                if ("boolean".equals(methodParameterInfo.getTypeDescriptor().toStringWithSimpleNames())) {
@@ -120,6 +117,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                }
 +            }
 +        }
-+        fail(this.overridesSetItemSlot.getName() + " needs to override setItemSlot with the boolean silent parameter as well");
++        fail(overridesSetItemSlot.getName() + " needs to override setItemSlot with the boolean silent parameter as well");
 +    }
 +}
diff --git a/patches/server/Fix-upstreams-block-state-factories.patch b/patches/server/Fix-upstreams-block-state-factories.patch
index 3dc5505053..565a5d229a 100644
--- a/patches/server/Fix-upstreams-block-state-factories.patch
+++ b/patches/server/Fix-upstreams-block-state-factories.patch
@@ -419,7 +419,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    @Test
 +    public void testBlockEntityTypes() {
 +        for (var blockEntityType : BuiltInRegistries.BLOCK_ENTITY_TYPE) {
-+            org.junit.Assert.assertNotNull(CraftBlockStates.getBlockStateType(blockEntityType));
++            org.junit.jupiter.api.Assertions.assertNotNull(CraftBlockStates.getBlockStateType(blockEntityType));
 +        }
 +    }
 +    // Paper end
diff --git a/patches/server/Get-entity-default-attributes.patch b/patches/server/Get-entity-default-attributes.patch
index 6f36411d9d..da530ff662 100644
--- a/patches/server/Get-entity-default-attributes.patch
+++ b/patches/server/Get-entity-default-attributes.patch
@@ -117,12 +117,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.attribute.AttributeModifier;
 +import org.bukkit.entity.EntityType;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertFalse;
-+import static org.junit.Assert.assertNotNull;
-+import static org.junit.Assert.assertThrows;
-+import static org.junit.Assert.assertTrue;
++import static org.junit.jupiter.api.Assertions.assertFalse;
++import static org.junit.jupiter.api.Assertions.assertNotNull;
++import static org.junit.jupiter.api.Assertions.assertThrows;
++import static org.junit.jupiter.api.Assertions.assertTrue;
 +
 +public class EntityTypeAttributesTest extends AbstractTestingBase {
 +
diff --git a/patches/server/Implement-Mob-Goal-API.patch b/patches/server/Implement-Mob-Goal-API.patch
index 8f8ea1f9e2..98044dbb03 100644
--- a/patches/server/Implement-Mob-Goal-API.patch
+++ b/patches/server/Implement-Mob-Goal-API.patch
@@ -13,8 +13,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3")
  
 +    testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test
-     testImplementation("junit:junit:4.13.2")
-     testImplementation("org.hamcrest:hamcrest-library:1.3")
+     testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
+     testImplementation("org.hamcrest:hamcrest:2.2")
  
 diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
 new file mode 100644
@@ -815,20 +815,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@
 +package com.destroystokyo.paper.entity.ai;
 +
-+import org.junit.Assert;
-+import org.junit.Test;
-+
++import io.github.classgraph.ClassGraph;
++import io.github.classgraph.ScanResult;
 +import java.lang.reflect.Field;
 +import java.lang.reflect.Modifier;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.stream.Collectors;
-+
 +import org.bukkit.entity.Mob;
++import org.junit.jupiter.api.Test;
 +
-+import io.github.classgraph.ClassGraph;
-+import io.github.classgraph.ScanResult;
++import static org.junit.jupiter.api.Assertions.assertNotEquals;
++import static org.junit.jupiter.api.Assertions.fail;
 +
 +public class VanillaMobGoalTest {
 +
@@ -874,7 +873,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            System.out.println("Missing from API: ");
 +            for (GoalKey<?> key : missingFromAPI) {
 +                System.out.println("GoalKey<" + key.getEntityClass().getSimpleName() + "> " + key.getNamespacedKey().getKey().toUpperCase() +
-+                                   " = GoalKey.of(" + key.getEntityClass().getSimpleName() + ".class, NamespacedKey.minecraft(\"" + key.getNamespacedKey().getKey() + "\"));");
++                    " = GoalKey.of(" + key.getEntityClass().getSimpleName() + ".class, NamespacedKey.minecraft(\"" + key.getNamespacedKey().getKey() + "\"));");
 +            }
 +            shouldFail = true;
 +        }
@@ -889,7 +888,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            deprecated.forEach(System.out::println);
 +        }
 +
-+        if (shouldFail) Assert.fail("See above");
++        if (shouldFail) {
++            fail("See above");
++        }
 +    }
 +
 +    private static boolean hasNoEnclosingClass(Class<?> clazz) {
@@ -902,7 +903,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft.world.entity").scan()) {
 +            classes = scanResult.getSubclasses("net.minecraft.world.entity.Mob").loadClasses();
 +        }
-+        Assert.assertNotEquals("There are supposed to be more than 0 entity types!", Collections.emptyList(), classes);
++        assertNotEquals(Collections.emptyList(), classes, "There are supposed to be more than 0 entity types!");
 +
 +        boolean shouldFail = false;
 +        for (Class<?> nmsClass : classes) {
@@ -913,6 +914,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            }
 +        }
 +
-+        if (shouldFail) Assert.fail("See above");
++        if (shouldFail) {
++            fail("See above");
++        }
 +    }
 +}
diff --git a/patches/server/Implement-Player-Client-Options-API.patch b/patches/server/Implement-Player-Client-Options-API.patch
index cd41a68b37..cd06414464 100644
--- a/patches/server/Implement-Player-Client-Options-API.patch
+++ b/patches/server/Implement-Player-Client-Options-API.patch
@@ -158,8 +158,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import com.destroystokyo.paper.ClientOption;
 +import net.minecraft.world.entity.player.ChatVisiblity;
 +import org.bukkit.Difficulty;
-+import org.junit.Assert;
-+import org.junit.Test;
++import org.junit.jupiter.api.Assertions;
++import org.junit.jupiter.api.Test;
 +
 +public class TranslationKeyTest {
 +
@@ -167,7 +167,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public void testChatVisibilityKeys() {
 +        for (ClientOption.ChatVisibility chatVisibility : ClientOption.ChatVisibility.values()) {
 +            if (chatVisibility == ClientOption.ChatVisibility.UNKNOWN) continue;
-+            Assert.assertEquals(chatVisibility + "'s translation key doesn't match", ChatVisiblity.valueOf(chatVisibility.name()).getKey(), chatVisibility.translationKey());
++            Assertions.assertEquals(chatVisibility + "'s translation key doesn't match", ChatVisiblity.valueOf(chatVisibility.name()).getKey(), chatVisibility.translationKey());
 +        }
 +    }
 +}
diff --git a/patches/server/Item-Rarity-API.patch b/patches/server/Item-Rarity-API.patch
index 465af2f235..db24dc99f6 100644
--- a/patches/server/Item-Rarity-API.patch
+++ b/patches/server/Item-Rarity-API.patch
@@ -41,23 +41,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +import io.papermc.paper.adventure.PaperAdventure;
 +import net.minecraft.world.item.Rarity;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertEquals;
 +
 +public class ItemRarityTest {
 +
 +    @Test
 +    public void testConvertFromNmsToBukkit() {
 +        for (Rarity nmsRarity : Rarity.values()) {
-+            assertEquals("rarity names are mis-matched", ItemRarity.values()[nmsRarity.ordinal()].name(), nmsRarity.name());
++            assertEquals(ItemRarity.values()[nmsRarity.ordinal()].name(), nmsRarity.name(), "rarity names are mis-matched");
 +        }
 +    }
 +
 +    @Test
 +    public void testRarityFormatting() {
 +        for (Rarity nmsRarity : Rarity.values()) {
-+            assertEquals("rarity formatting is mis-matched", nmsRarity.color, PaperAdventure.asVanilla(ItemRarity.values()[nmsRarity.ordinal()].color));
++            assertEquals(nmsRarity.color, PaperAdventure.asVanilla(ItemRarity.values()[nmsRarity.ordinal()].color), "rarity formatting is mis-matched");
 +        }
 +    }
 +}
diff --git a/patches/server/ItemStack-repair-check-API.patch b/patches/server/ItemStack-repair-check-API.patch
index 69f1d264c9..fb1bb5e948 100644
--- a/patches/server/ItemStack-repair-check-API.patch
+++ b/patches/server/ItemStack-repair-check-API.patch
@@ -34,11 +34,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.Material;
 +import org.bukkit.inventory.ItemStack;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertFalse;
-+import static org.junit.Assert.assertThrows;
-+import static org.junit.Assert.assertTrue;
++import static org.junit.jupiter.api.Assertions.assertFalse;
++import static org.junit.jupiter.api.Assertions.assertThrows;
++import static org.junit.jupiter.api.Assertions.assertTrue;
 +
 +public class ItemStackRepairCheckTest extends AbstractTestingBase {
 +
@@ -46,34 +46,34 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public void testIsRepariableBy() {
 +        ItemStack diamondPick = new ItemStack(Material.DIAMOND_PICKAXE);
 +
-+        assertTrue("diamond pick isn't repairable by a diamond", diamondPick.isRepairableBy(new ItemStack(Material.DIAMOND)));
++        assertTrue(diamondPick.isRepairableBy(new ItemStack(Material.DIAMOND)), "diamond pick isn't repairable by a diamond");
 +    }
 +
 +    @Test
 +    public void testCanRepair() {
 +        ItemStack diamond = new ItemStack(Material.DIAMOND);
 +
-+        assertTrue("diamond can't repair a diamond axe", diamond.canRepair(new ItemStack(Material.DIAMOND_AXE)));
++        assertTrue(diamond.canRepair(new ItemStack(Material.DIAMOND_AXE)), "diamond can't repair a diamond axe");
 +    }
 +
 +    @Test
 +    public void testIsNotRepairableBy() {
 +        ItemStack notDiamondPick = new ItemStack(Material.ACACIA_SAPLING);
 +
-+        assertFalse("acacia sapling is repairable by a diamond", notDiamondPick.isRepairableBy(new ItemStack(Material.DIAMOND)));
++        assertFalse(notDiamondPick.isRepairableBy(new ItemStack(Material.DIAMOND)), "acacia sapling is repairable by a diamond");
 +    }
 +
 +    @Test
 +    public void testCanNotRepair() {
 +        ItemStack diamond = new ItemStack(Material.DIAMOND);
 +
-+        assertFalse("diamond can repair oak button", diamond.canRepair(new ItemStack(Material.OAK_BUTTON)));
++        assertFalse(diamond.canRepair(new ItemStack(Material.OAK_BUTTON)), "diamond can repair oak button");
 +    }
 +
 +    @Test
 +    public void testInvalidItem() {
 +        ItemStack badItemStack = new ItemStack(Material.ACACIA_WALL_SIGN);
 +
-+        assertFalse("acacia wall sign is repairable by diamond", badItemStack.isRepairableBy(new ItemStack(Material.DIAMOND)));
++        assertFalse(badItemStack.isRepairableBy(new ItemStack(Material.DIAMOND)), "acacia wall sign is repairable by diamond");
 +    }
 +}
diff --git a/patches/server/More-Enchantment-API.patch b/patches/server/More-Enchantment-API.patch
index 5d88ba2d7a..d44d49def3 100644
--- a/patches/server/More-Enchantment-API.patch
+++ b/patches/server/More-Enchantment-API.patch
@@ -101,9 +101,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +import net.minecraft.world.item.enchantment.Enchantment.Rarity;
 +import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertNotNull;
++import static org.junit.jupiter.api.Assertions.assertNotNull;
 +
 +public class EnchantmentRarityTest {
 +
@@ -129,13 +129,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import net.minecraft.world.entity.MobType;
 +import org.bukkit.craftbukkit.entity.CraftLivingEntity;
 +import org.bukkit.entity.EntityCategory;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
 +import java.lang.reflect.Field;
 +import java.util.Map;
 +import java.util.Set;
 +
-+import static org.junit.Assert.assertTrue;
++import static org.junit.jupiter.api.Assertions.assertTrue;
 +
 +public class EntityCategoryTest {
 +
@@ -152,6 +152,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        for (EntityCategory entityCategory : EntityCategory.values()) {
 +            enumMonsterTypeFieldMap.remove(CraftLivingEntity.fromBukkitEntityCategory(entityCategory));
 +        }
-+        assertTrue(MobType.class.getName() + " instance(s): " + Joiner.on(", ").join(enumMonsterTypeFieldMap.values()) + " do not have bukkit equivalents", enumMonsterTypeFieldMap.size() == 0);
++        assertTrue(enumMonsterTypeFieldMap.size() == 0, MobType.class.getName() + " instance(s): " + Joiner.on(", ").join(enumMonsterTypeFieldMap.values()) + " do not have bukkit equivalents");
 +    }
 +}
diff --git a/patches/server/More-PotionEffectType-API.patch b/patches/server/More-PotionEffectType-API.patch
index 882de5e807..6992b5e226 100644
--- a/patches/server/More-PotionEffectType-API.patch
+++ b/patches/server/More-PotionEffectType-API.patch
@@ -72,17 +72,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import net.minecraft.world.effect.MobEffectCategory;
 +import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
 +import org.bukkit.potion.PotionEffectType;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
-+import static org.junit.Assert.assertEquals;
-+import static org.junit.Assert.assertNotNull;
++import static org.junit.jupiter.api.Assertions.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertNotNull;
 +
 +public class EffectCategoryTest {
 +
 +    @Test
 +    public void testEffectCategoriesExist() {
 +        for (MobEffectCategory mobEffectInfo : MobEffectCategory.values()) {
-+            assertNotNull(mobEffectInfo + " is missing a bukkit equivalent", CraftPotionEffectType.fromNMS(mobEffectInfo));
++            assertNotNull(CraftPotionEffectType.fromNMS(mobEffectInfo), mobEffectInfo + " is missing a bukkit equivalent");
 +        }
 +    }
 +
@@ -90,7 +90,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public void testCategoryHasEquivalentColors() {
 +        for (MobEffectCategory mobEffectInfo : MobEffectCategory.values()) {
 +            PotionEffectType.Category bukkitEffectCategory = CraftPotionEffectType.fromNMS(mobEffectInfo);
-+            assertEquals(mobEffectInfo.getTooltipFormatting().name() + " doesn't equal " + bukkitEffectCategory.getColor(), bukkitEffectCategory.getColor(), PaperAdventure.asAdventure(mobEffectInfo.getTooltipFormatting()));
++            assertEquals(bukkitEffectCategory.getColor(), PaperAdventure.asAdventure(mobEffectInfo.getTooltipFormatting()), mobEffectInfo.getTooltipFormatting().name() + " doesn't equal " + bukkitEffectCategory.getColor());
 +        }
 +    }
 +}
diff --git a/patches/server/Paper-Plugins.patch b/patches/server/Paper-Plugins.patch
index 21be113acb..43f152434b 100644
--- a/patches/server/Paper-Plugins.patch
+++ b/patches/server/Paper-Plugins.patch
@@ -7281,7 +7281,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +package io.papermc.paper.plugin;
 +
 +import io.papermc.paper.plugin.entrypoint.dependency.MetaDependencyTree;
-+import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
 +import java.util.List;
 +
@@ -7351,9 +7351,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import io.papermc.paper.plugin.entrypoint.strategy.modern.ModernPluginLoadingStrategy;
 +import io.papermc.paper.plugin.entrypoint.strategy.ProviderConfiguration;
 +import io.papermc.paper.plugin.provider.PluginProvider;
-+import org.junit.Assert;
-+import org.junit.Before;
-+import org.junit.Test;
++import org.junit.jupiter.api.Assertions;
++import org.junit.jupiter.api.BeforeEach;
++import org.junit.jupiter.api.Test;
 +
 +import java.util.ArrayList;
 +import java.util.HashMap;
@@ -7434,7 +7434,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        setup("RedFire", new String[]{"BrightBlueGrass", "BigPaper"}, new String[]{"BlueFire", "GreenGlass", "BigGrass"}, EMPTY);
 +    }
 +
-+    @Before
++    @BeforeEach
 +    public void loadProviders() {
 +        AtomicInteger currentLoad = new AtomicInteger();
 +        ModernPluginLoadingStrategy<PaperTestPlugin> modernPluginLoadingStrategy = new ModernPluginLoadingStrategy<>(new ProviderConfiguration<>() {
@@ -7458,16 +7458,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        for (PluginProvider<PaperTestPlugin> provider : REGISTERED_PROVIDERS) {
 +            TestPluginMeta pluginMeta = (TestPluginMeta) provider.getMeta();
 +            String identifier = pluginMeta.getName();
-+            Assert.assertTrue("Provider wasn't loaded! (%s)".formatted(identifier), LOAD_ORDER.containsKey(identifier));
++            Assertions.assertTrue(LOAD_ORDER.containsKey(identifier), "Provider wasn't loaded! (%s)".formatted(identifier));
 +
 +            int index = LOAD_ORDER.get(identifier);
 +
 +            // Hard dependencies should be loaded BEFORE
 +            for (String hardDependency : pluginMeta.getPluginDependencies()) {
-+                Assert.assertTrue("Plugin (%s) is missing hard dependency (%s)".formatted(identifier, hardDependency), LOAD_ORDER.containsKey(hardDependency));
++                Assertions.assertTrue(LOAD_ORDER.containsKey(hardDependency), "Plugin (%s) is missing hard dependency (%s)".formatted(identifier, hardDependency));
 +
 +                int dependencyIndex = LOAD_ORDER.get(hardDependency);
-+                Assert.assertTrue("Plugin (%s) was not loaded BEFORE soft dependency. (%s)".formatted(identifier, hardDependency), index > dependencyIndex);
++                Assertions.assertTrue(index > dependencyIndex, "Plugin (%s) was not loaded BEFORE soft dependency. (%s)".formatted(identifier, hardDependency));
 +            }
 +
 +            for (String softDependency : pluginMeta.getPluginSoftDependencies()) {
@@ -7477,7 +7477,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +                int dependencyIndex = LOAD_ORDER.get(softDependency);
 +
-+                Assert.assertTrue("Plugin (%s) was not loaded BEFORE soft dependency. (%s)".formatted(identifier, softDependency), index > dependencyIndex);
++                Assertions.assertTrue(index > dependencyIndex, "Plugin (%s) was not loaded BEFORE soft dependency. (%s)".formatted(identifier, softDependency));
 +            }
 +
 +            for (String loadBefore : pluginMeta.getLoadBeforePlugins()) {
@@ -7486,7 +7486,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                }
 +
 +                int dependencyIndex = LOAD_ORDER.get(loadBefore);
-+                Assert.assertTrue("Plugin (%s) was NOT loaded BEFORE loadbefore dependency. (%s)".formatted(identifier, loadBefore), index < dependencyIndex);
++                Assertions.assertTrue(index < dependencyIndex, "Plugin (%s) was NOT loaded BEFORE loadbefore dependency. (%s)".formatted(identifier, loadBefore));
 +            }
 +        }
 +    }
@@ -7504,8 +7504,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.permissions.Permission;
 +import org.bukkit.plugin.PluginManager;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.After;
-+import org.junit.Test;
++import org.junit.jupiter.api.AfterEach;
++import org.junit.jupiter.api.Test;
 +
 +import static org.hamcrest.MatcherAssert.assertThat;
 +import static org.hamcrest.Matchers.*;
@@ -7566,7 +7566,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        assertThat("Permission \"" + name + "\" was not removed", pm.getPermission(name), is(nullValue()));
 +    }
 +
-+    @After
++    @AfterEach
 +    public void tearDown() {
 +        pm.clearPlugins();
 +        assertThat(pm.getPermissions(), is(empty()));
@@ -7581,8 +7581,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +package io.papermc.paper.plugin;
 +
 +import io.papermc.paper.plugin.provider.configuration.PaperPluginMeta;
-+import org.junit.Assert;
-+import org.junit.Test;
++import org.junit.jupiter.api.Assertions;
++import org.junit.jupiter.api.Test;
 +
 +public class PluginNamingTest {
 +    private static final String TEST_NAME = "Test_Plugin";
@@ -7598,12 +7598,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +    @Test
 +    public void testName() {
-+        Assert.assertEquals(TEST_NAME, this.pluginMeta.getName());
++        Assertions.assertEquals(TEST_NAME, this.pluginMeta.getName());
 +    }
 +
 +    @Test
 +    public void testDisplayName() {
-+        Assert.assertEquals(TEST_NAME + " v" + TEST_VERSION, this.pluginMeta.getDisplayName());
++        Assertions.assertEquals(TEST_NAME + " v" + TEST_VERSION, this.pluginMeta.getDisplayName());
 +    }
 +}
 diff --git a/src/test/java/io/papermc/paper/plugin/SyntheticEventTest.java b/src/test/java/io/papermc/paper/plugin/SyntheticEventTest.java
@@ -7619,8 +7619,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import org.bukkit.event.Event;
 +import org.bukkit.event.EventHandler;
 +import org.bukkit.event.Listener;
-+import org.junit.Assert;
-+import org.junit.Test;
++import org.junit.jupiter.api.Assertions;
++import org.junit.jupiter.api.Test;
 +
 +public class SyntheticEventTest {
 +
@@ -7635,7 +7635,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        paperPluginManager.registerEvents(impl, paperTestPlugin);
 +        paperPluginManager.callEvent(event);
 +
-+        Assert.assertEquals(1, impl.callCount);
++        Assertions.assertEquals(1, impl.callCount);
 +    }
 +
 +    public abstract static class Base<E extends Event> implements Listener {
diff --git a/patches/server/Properly-handle-BlockBreakEvent-isDropItems.patch b/patches/server/Properly-handle-BlockBreakEvent-isDropItems.patch
index bf2516999e..1effedf53a 100644
--- a/patches/server/Properly-handle-BlockBreakEvent-isDropItems.patch
+++ b/patches/server/Properly-handle-BlockBreakEvent-isDropItems.patch
@@ -122,21 +122,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +import io.github.classgraph.ScanResult;
 +import java.util.ArrayList;
 +import java.util.List;
++import java.util.stream.Stream;
 +import org.bukkit.support.AbstractTestingBase;
-+import org.junit.Test;
-+import org.junit.runner.RunWith;
-+import org.junit.runners.Parameterized;
++import org.junit.jupiter.params.ParameterizedTest;
++import org.junit.jupiter.params.provider.MethodSource;
 +
-+import static org.junit.Assert.assertEquals;
++import static org.junit.jupiter.api.Assertions.assertEquals;
 +
-+@RunWith(Parameterized.class)
 +public class BlockPlayerDestroyOverrideTest extends AbstractTestingBase {
 +
-+    @Parameterized.Parameter
-+    public ClassInfo overridesPlayerDestroy;
-+
-+    @Parameterized.Parameters
-+    public static Iterable<ClassInfo> parameters() {
++    @MethodSource("parameters")
++    public static Stream<ClassInfo> parameters() {
 +        final List<ClassInfo> classInfo = new ArrayList<>();
 +        try (ScanResult scanResult = new ClassGraph()
 +            .enableClassInfo()
@@ -151,15 +147,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                }
 +            }
 +        }
-+        return classInfo;
++        return classInfo.stream();
 +    }
 +
-+    @Test
-+    public void checkPlayerDestroyOverrides() {
-+        final MethodInfoList playerDestroy = this.overridesPlayerDestroy.getDeclaredMethodInfo("playerDestroy");
-+        assertEquals(this.overridesPlayerDestroy.getName() + " has multiple playerDestroy methods", 1, playerDestroy.size());
++    @ParameterizedTest
++    @MethodSource("parameters")
++    public void checkPlayerDestroyOverrides(ClassInfo overridesPlayerDestroy) {
++        final MethodInfoList playerDestroy = overridesPlayerDestroy.getDeclaredMethodInfo("playerDestroy");
++        assertEquals(1, playerDestroy.size(), overridesPlayerDestroy.getName() + " has multiple playerDestroy methods");
 +        final MethodInfo next = playerDestroy.iterator().next();
 +        final MethodParameterInfo[] parameterInfo = next.getParameterInfo();
-+        assertEquals(this.overridesPlayerDestroy.getName() + " needs to change it's override of playerDestroy", "boolean", parameterInfo[parameterInfo.length - 1].getTypeDescriptor().toStringWithSimpleNames());
++        assertEquals(overridesPlayerDestroy.getName() + " needs to change its override of playerDestroy", "boolean", parameterInfo[parameterInfo.length - 1].getTypeDescriptor().toStringWithSimpleNames());
 +    }
 +}
diff --git a/patches/server/Remap-fixes.patch b/patches/server/Remap-fixes.patch
index 6c4101c29d..0bd580edf6 100644
--- a/patches/server/Remap-fixes.patch
+++ b/patches/server/Remap-fixes.patch
@@ -92,29 +92,29 @@ diff --git a/src/test/java/org/bukkit/DyeColorsTest.java b/src/test/java/org/buk
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/test/java/org/bukkit/DyeColorsTest.java
 +++ b/src/test/java/org/bukkit/DyeColorsTest.java
-@@ -0,0 +0,0 @@ import static org.hamcrest.Matchers.*;
- import static org.junit.Assert.*;
- import java.util.ArrayList;
- import java.util.List;
+@@ -0,0 +0,0 @@ package org.bukkit;
+ import static org.bukkit.support.MatcherAssert.*;
+ import static org.hamcrest.Matchers.*;
+ 
 -import net.minecraft.world.item.DyeColor;
  import org.bukkit.support.AbstractTestingBase;
- import org.junit.Test;
- import org.junit.runner.RunWith;
+ import org.junit.jupiter.params.ParameterizedTest;
+ import org.junit.jupiter.params.provider.EnumSource;
 @@ -0,0 +0,0 @@ public class DyeColorsTest extends AbstractTestingBase {
-     @Test
-     public void checkColor() {
-         Color color = this.dye.getColor();
--        float[] nmsColorArray = DyeColor.byId(this.dye.getWoolData()).getTextureDiffuseColors();
-+        float[] nmsColorArray = net.minecraft.world.item.DyeColor.byId(this.dye.getWoolData()).getTextureDiffuseColors(); // Paper - remap fix
+     @EnumSource(DyeColor.class)
+     public void checkColor(DyeColor dye) {
+         Color color = dye.getColor();
+-        float[] nmsColorArray = DyeColor.byId(dye.getWoolData()).getTextureDiffuseColors();
++        float[] nmsColorArray = net.minecraft.world.item.DyeColor.byId(dye.getWoolData()).getTextureDiffuseColors(); // Paper - remap fix
          Color nmsColor = Color.fromRGB((int) (nmsColorArray[0] * 255), (int) (nmsColorArray[1] * 255), (int) (nmsColorArray[2] * 255));
          assertThat(color, is(nmsColor));
      }
 @@ -0,0 +0,0 @@ public class DyeColorsTest extends AbstractTestingBase {
-     @Test
-     public void checkFireworkColor() {
-         Color color = this.dye.getFireworkColor();
--        int nmsColor = DyeColor.byId(this.dye.getWoolData()).getFireworkColor();
-+        int nmsColor = net.minecraft.world.item.DyeColor.byId(this.dye.getWoolData()).getFireworkColor(); // Paper - remap fix
+     @EnumSource(org.bukkit.DyeColor.class)
+     public void checkFireworkColor(org.bukkit.DyeColor dye) {
+         Color color = dye.getFireworkColor();
+-        int nmsColor = DyeColor.byId(dye.getWoolData()).getFireworkColor();
++        int nmsColor = net.minecraft.world.item.DyeColor.byId(dye.getWoolData()).getFireworkColor(); // Paper - remap fix
          assertThat(color, is(Color.fromRGB(nmsColor)));
      }
  }
@@ -153,8 +153,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  import net.minecraft.resources.ResourceLocation;
 -import net.minecraft.world.entity.EntityType;
  import org.bukkit.support.AbstractTestingBase;
- import org.junit.Assert;
- import org.junit.Test;
+ import org.junit.jupiter.api.Test;
+ 
 @@ -0,0 +0,0 @@ public class EntityTypesTest extends AbstractTestingBase {
      public void testMaps() {
          Set<EntityType> allBukkit = Arrays.stream(EntityType.values()).filter((b) -> b.getName() != null).collect(Collectors.toSet());
@@ -165,27 +165,30 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            ResourceLocation key = net.minecraft.world.entity.EntityType.getKey(nms); // Paper - remap fix
  
              org.bukkit.entity.EntityType bukkit = org.bukkit.entity.EntityType.fromName(key.getPath());
-             Assert.assertNotNull("Missing nms->bukkit " + key, bukkit);
+             assertNotNull(bukkit, "Missing nms->bukkit " + key);
 diff --git a/src/test/java/org/bukkit/entity/PandaGeneTest.java b/src/test/java/org/bukkit/entity/PandaGeneTest.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/test/java/org/bukkit/entity/PandaGeneTest.java
 +++ b/src/test/java/org/bukkit/entity/PandaGeneTest.java
-@@ -0,0 +0,0 @@
- package org.bukkit.entity;
+@@ -0,0 +0,0 @@ package org.bukkit.entity;
+ 
+ import static org.junit.jupiter.api.Assertions.*;
  
 -import net.minecraft.world.entity.animal.Panda;
  import org.bukkit.craftbukkit.entity.CraftPanda;
- import org.junit.Assert;
- import org.junit.Test;
+ import org.junit.jupiter.api.Test;
+ 
 @@ -0,0 +0,0 @@ public class PandaGeneTest {
+ 
      @Test
      public void testBukkit() {
-         for (Panda.Gene gene : Panda.Gene.values()) {
+-        for (Panda.Gene gene : Panda.Gene.values()) {
 -            Panda.Gene nms = CraftPanda.toNms(gene);
++        for (Panda.Gene gene : Panda.Gene.values()) { // Paper - remap fix
 +            net.minecraft.world.entity.animal.Panda.Gene nms = CraftPanda.toNms(gene); // Paper - remap fix
  
-             Assert.assertNotNull("NMS gene null for " + gene, nms);
-             Assert.assertEquals("Recessive status did not match " + gene, gene.isRecessive(), nms.isRecessive());
+             assertNotNull(nms, "NMS gene null for " + gene);
+             assertEquals(gene.isRecessive(), nms.isRecessive(), "Recessive status did not match " + gene);
 @@ -0,0 +0,0 @@ public class PandaGeneTest {
  
      @Test
@@ -194,4 +197,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        for (net.minecraft.world.entity.animal.Panda.Gene gene : net.minecraft.world.entity.animal.Panda.Gene.values()) { // Paper - remap fix
              org.bukkit.entity.Panda.Gene bukkit = CraftPanda.fromNms(gene);
  
-             Assert.assertNotNull("Bukkit gene null for " + gene, bukkit);
+             assertNotNull(bukkit, "Bukkit gene null for " + gene);
diff --git a/patches/server/Setup-Gradle-project.patch b/patches/server/Setup-Gradle-project.patch
index 867bc37ba7..0330582c8d 100644
--- a/patches/server/Setup-Gradle-project.patch
+++ b/patches/server/Setup-Gradle-project.patch
@@ -55,8 +55,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3")
 +    runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3")
 +
-+    testImplementation("junit:junit:4.13.2")
-+    testImplementation("org.hamcrest:hamcrest-library:1.3")
++    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
++    testImplementation("org.hamcrest:hamcrest:2.2")
 +}
 +
 +val craftbukkitPackageVersion = "1_20_R2" // Paper
@@ -402,6 +402,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            <artifactId>json-simple</artifactId>
 -            <version>1.1.1</version>
 -            <scope>runtime</scope>
+-            <exclusions>
+-                <exclusion>
+-                    <groupId>junit</groupId>
+-                    <artifactId>junit</artifactId>
+-                </exclusion>
+-            </exclusions>
 -        </dependency>
 -        <dependency>
 -            <groupId>org.xerial</groupId>
@@ -443,15 +449,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        </dependency>
 -        <!-- testing -->
 -        <dependency>
--            <groupId>junit</groupId>
--            <artifactId>junit</artifactId>
--            <version>4.13.2</version>
+-            <groupId>org.junit.jupiter</groupId>
+-            <artifactId>junit-jupiter</artifactId>
+-            <version>5.10.0</version>
 -            <scope>test</scope>
 -        </dependency>
 -        <dependency>
 -            <groupId>org.hamcrest</groupId>
--            <artifactId>hamcrest-library</artifactId>
--            <version>1.3</version>
+-            <artifactId>hamcrest</artifactId>
+-            <version>2.2</version>
 -            <scope>test</scope>
 -        </dependency>
 -    </dependencies>
diff --git a/patches/server/fix-item-meta-for-tadpole-buckets.patch b/patches/server/fix-item-meta-for-tadpole-buckets.patch
index 58904d3ba6..160d162f68 100644
--- a/patches/server/fix-item-meta-for-tadpole-buckets.patch
+++ b/patches/server/fix-item-meta-for-tadpole-buckets.patch
@@ -59,11 +59,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        for (final Item item : BuiltInRegistries.ITEM) {
 +            if (item instanceof net.minecraft.world.item.HangingEntityItem || item instanceof net.minecraft.world.item.MobBucketItem) {
 +                ItemStack stack = new ItemStack(CraftMagicNumbers.getMaterial(item));
-+                assertTrue("missing entity tag meta handling for " + item, ENTITY_TAG_METAS.contains(stack.getItemMeta().getClass()));
++                assertTrue(ENTITY_TAG_METAS.contains(stack.getItemMeta().getClass()), "missing entity tag meta handling for " + item);
 +                stack = CraftItemStack.asNewCraftStack(net.minecraft.world.item.Items.STONE);
 +                stack.editMeta(meta -> meta.displayName(net.kyori.adventure.text.Component.text("hello")));
 +                stack.setType(CraftMagicNumbers.getMaterial(item));
-+                assertTrue("missing entity tag meta handling for " + item, ENTITY_TAG_METAS.contains(stack.getItemMeta().getClass()));
++                assertTrue(ENTITY_TAG_METAS.contains(stack.getItemMeta().getClass()), "missing entity tag meta handling for " + item);
 +            }
 +        }
 +    }
diff --git a/work/Bukkit b/work/Bukkit
index dfe1fb4853..fb23cbb382 160000
--- a/work/Bukkit
+++ b/work/Bukkit
@@ -1 +1 @@
-Subproject commit dfe1fb4853158bd17f6955527ad3bf85f4d5150d
+Subproject commit fb23cbb3829ef866094f45ffc5391d516e6a3c57
diff --git a/work/CraftBukkit b/work/CraftBukkit
index f71a799f03..f0661c3514 160000
--- a/work/CraftBukkit
+++ b/work/CraftBukkit
@@ -1 +1 @@
-Subproject commit f71a799f03aae4277a48b4a1082b478833975682
+Subproject commit f0661c3514a7d8e51e2281f045e1c14d0e733230