From 7791295abf60d66c1f655cae95e8715d8e18281f Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 29 May 2026 17:25:54 -0700 Subject: [PATCH 01/12] Pin MockBukkit to Maven Central 4.110.0 instead of jitpack snapshot The floating jitpack snapshot v1.21-SNAPSHOT resolves to an ephemeral git-described build whose jar/POM get evicted, breaking CI even on unchanged commits. Switch to the equivalent stable Maven Central coordinates (org.mockbukkit.mockbukkit:mockbukkit-v1.21:4.110.0). Co-Authored-By: Claude Opus 4.8 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 5c7a0d3..dfde37e 100644 --- a/pom.xml +++ b/pom.xml @@ -142,9 +142,9 @@ - com.github.MockBukkit - MockBukkit - v1.21-SNAPSHOT + org.mockbukkit.mockbukkit + mockbukkit-v1.21 + 4.110.0 test From c2d1258c53648f1392301ccc1e7b70a89be4b741 Mon Sep 17 00:00:00 2001 From: tastybento Date: Wed, 3 Jun 2026 08:07:57 -0700 Subject: [PATCH 02/12] Update Modrinth game-versions to current MC range Brings the supported-versions list in line with the other addons (1.21.5-1.21.11, 26.1, 26.1.1, 26.1.2). Co-Authored-By: Claude Opus 4.8 --- .github/workflows/modrinth-publish.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/modrinth-publish.yml b/.github/workflows/modrinth-publish.yml index 0a5be0e..6d73ddf 100644 --- a/.github/workflows/modrinth-publish.yml +++ b/.github/workflows/modrinth-publish.yml @@ -38,6 +38,14 @@ jobs: paper spigot game-versions: |- - 1.21.4 1.21.5 + 1.21.6 + 1.21.7 + 1.21.8 + 1.21.9 + 1.21.10 + 1.21.11 + 26.1 + 26.1.1 + 26.1.2 files: ${{ github.workspace }}/target/Border-${{ github.event.release.tag_name }}.jar From eb42b956bd58bd5ce7bcf45b96135b24de7257b6 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 27 Jun 2026 17:26:19 -0700 Subject: [PATCH 03/12] Add files via upload --- .github/workflows/publish.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..91bc434 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,19 @@ +# Border — .github/workflows/publish.yml +# CurseForge-first rollout. Hangar left blank; add its slug later to enable Hangar. + +name: Publish release + +on: + release: + types: [published] + +jobs: + publish: + uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@main + with: + hangar_slug: "" + curseforge_id: "1515484" + game_versions: "26.1.2,26.1.1,26.1,1.21.11,1.21.10,1.21.9,1.21.8,1.21.7,1.21.6" + secrets: + HANGAR_API_KEY: ${{ secrets.HANGAR_API_KEY }} + CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} From 302f1c612d47d20bc4baa79b0516145beaca6267 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 28 Jun 2026 09:00:54 -0700 Subject: [PATCH 04/12] ci: add CurseForge publish workflow (publishes the release asset) Publish the jar attached to each GitHub release to CurseForge via the shared BentoBoxWorld/.github reusable workflow, downloading the release asset instead of rebuilding from source. Mirrors the BentoBox core setup. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/publish.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 91bc434..a2c0a51 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,19 +1,30 @@ # Border — .github/workflows/publish.yml -# CurseForge-first rollout. Hangar left blank; add its slug later to enable Hangar. +# Publishes the jar attached to a GitHub release to CurseForge (and Hangar once a +# slug is set) via the shared BentoBoxWorld/.github reusable workflow. It downloads +# the release asset rather than rebuilding from source, so a dependency-repo outage +# can't block publishing. Includes workflow_dispatch to (re)publish a given version. -name: Publish release +name: Publish release to CurseForge on: release: types: [published] + workflow_dispatch: + inputs: + version: + description: "Version to publish (e.g. 1.2.3)" + required: true + type: string jobs: publish: - uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@main + uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@master with: - hangar_slug: "" + use_release_asset: "true" # publish the jar attached to the release; do not rebuild + hangar_slug: "" # set the Hangar project slug to enable Hangar publishing curseforge_id: "1515484" - game_versions: "26.1.2,26.1.1,26.1,1.21.11,1.21.10,1.21.9,1.21.8,1.21.7,1.21.6" + game_versions: "26.2,26.1.2,26.1.1,26.1,1.21.11,1.21.10,1.21.9,1.21.8,1.21.7,1.21.6,1.21.5" + version: ${{ inputs.version }} # empty on release events -> falls back to the release tag secrets: HANGAR_API_KEY: ${{ secrets.HANGAR_API_KEY }} CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} From 7b8bbc0f34c9164ed3dc6215c42490c4b1988a08 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 28 Jun 2026 11:16:14 -0700 Subject: [PATCH 05/12] ci: pin reusable workflow to SHA and enable Hangar publish Pin bentoboxworld/.github publish-platforms.yml to commit e0c5d98f5e6ef9ea7c9a28afad05f4c07bcde898 (Sonar githubactions:S7637) and set the Hangar slug so releases publish to Hangar alongside CurseForge. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/publish.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a2c0a51..9213462 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,10 +1,10 @@ # Border — .github/workflows/publish.yml -# Publishes the jar attached to a GitHub release to CurseForge (and Hangar once a -# slug is set) via the shared BentoBoxWorld/.github reusable workflow. It downloads -# the release asset rather than rebuilding from source, so a dependency-repo outage -# can't block publishing. Includes workflow_dispatch to (re)publish a given version. +# Publishes the jar attached to a GitHub release to CurseForge and Hangar via the +# shared BentoBoxWorld/.github reusable workflow. Downloads the release asset instead of +# rebuilding from source. The reusable workflow is pinned to a commit SHA (Sonar +# githubactions:S7637). workflow_dispatch lets you (re)publish a given version. -name: Publish release to CurseForge +name: Publish release to CurseForge and Hangar on: release: @@ -18,10 +18,10 @@ on: jobs: publish: - uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@master + uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@e0c5d98f5e6ef9ea7c9a28afad05f4c07bcde898 # master with: use_release_asset: "true" # publish the jar attached to the release; do not rebuild - hangar_slug: "" # set the Hangar project slug to enable Hangar publishing + hangar_slug: "Border" # blank = skip Hangar curseforge_id: "1515484" game_versions: "26.2,26.1.2,26.1.1,26.1,1.21.11,1.21.10,1.21.9,1.21.8,1.21.7,1.21.6,1.21.5" version: ${{ inputs.version }} # empty on release events -> falls back to the release tag From 881eb966ccc486016b2dc4684c96e03e87a56ae4 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 28 Jun 2026 11:50:55 -0700 Subject: [PATCH 06/12] ci: bump pinned reusable workflow to 71bf927bce32586216baa6995f21852d944b98b9 Picks up the split into independent CurseForge and Hangar jobs so neither platform can block the other. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9213462..4d3f54b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ on: jobs: publish: - uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@e0c5d98f5e6ef9ea7c9a28afad05f4c07bcde898 # master + uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@71bf927bce32586216baa6995f21852d944b98b9 # master with: use_release_asset: "true" # publish the jar attached to the release; do not rebuild hangar_slug: "Border" # blank = skip Hangar From 7e7370b80508819ba81049f62977facccaadfd15 Mon Sep 17 00:00:00 2001 From: tastybento Date: Tue, 30 Jun 2026 23:15:03 -0700 Subject: [PATCH 07/12] ci: bump pinned reusable workflow to fe4b1f0 --- .github/workflows/publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4d3f54b..01a9572 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ on: jobs: publish: - uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@71bf927bce32586216baa6995f21852d944b98b9 # master + uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@fe4b1f03c19f4fd4212020a06a07a7097923adec # master with: use_release_asset: "true" # publish the jar attached to the release; do not rebuild hangar_slug: "Border" # blank = skip Hangar @@ -27,4 +27,4 @@ jobs: version: ${{ inputs.version }} # empty on release events -> falls back to the release tag secrets: HANGAR_API_KEY: ${{ secrets.HANGAR_API_KEY }} - CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} + CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} \ No newline at end of file From 8eefdf5ea157450581468961840ca8d947581158 Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 3 Jul 2026 11:54:15 -0700 Subject: [PATCH 08/12] ci: bump pinned publish-platforms.yml to ca2dcd1 --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 01a9572..cd1b3c9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ on: jobs: publish: - uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@fe4b1f03c19f4fd4212020a06a07a7097923adec # master + uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@ca2dcd167e8db4e0f671a976080744dda43801a6 # master with: use_release_asset: "true" # publish the jar attached to the release; do not rebuild hangar_slug: "Border" # blank = skip Hangar From a54d174908b3bbd04d9a8b7ae496d9a354e7f46b Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 27 Jul 2026 08:34:23 -0700 Subject: [PATCH 09/12] Declare the border colour and bordertype permissions The permissions the commands actually check were missing from addon.yml, so they had no declared default and fell back to op only - which contradicts the documentation and stops a player from using /[gamemode] border color at all. [gamemode].bordertype was declared but never checked: BorderTypeCommand asks for border. + its label, i.e. [gamemode].border.bordertype. Found while tracing BentoBoxWorld/Border#179. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01E6rYr6Z66LQz9XTbnbUupy --- src/main/resources/addon.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml index aca8f56..a0d2570 100644 --- a/src/main/resources/addon.yml +++ b/src/main/resources/addon.yml @@ -15,6 +15,18 @@ permissions: '[gamemode].border.type': description: Player can use border type setting command default: true - '[gamemode].bordertype': + '[gamemode].border.bordertype': description: Player can use bordertype command to change the border type - default: false \ No newline at end of file + default: false + '[gamemode].border.color': + description: Player can use border color setting command + default: true + '[gamemode].border.color.red': + description: Player can set their border color to red + default: op + '[gamemode].border.color.green': + description: Player can set their border color to green + default: op + '[gamemode].border.color.blue': + description: Player can set their border color to blue + default: op \ No newline at end of file From fc72038b5e9584778504a4b35beddfb3886ad9c0 Mon Sep 17 00:00:00 2001 From: tastybento Date: Wed, 29 Jul 2026 09:07:20 -0700 Subject: [PATCH 10/12] ci: bump publish-platforms pin to the multipart-JSON fix The pinned SHA predates BentoBoxWorld/.github#13, which moved the CurseForge metadata and Hangar versionUpload JSON out of inline curl -F values and into files. curl treats a ';' in an inline -F value as the start of a type= attribute, so a release body containing a semicolon truncates the JSON and both platforms reject the upload. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01SAQ3YQkcfRoCZEwa6SPJcp --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cd1b3c9..10dbca7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ on: jobs: publish: - uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@ca2dcd167e8db4e0f671a976080744dda43801a6 # master + uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@1f91a0edf72e8c86d671b3b8fdd3121ac6fb88e1 # master with: use_release_asset: "true" # publish the jar attached to the release; do not rebuild hangar_slug: "Border" # blank = skip Hangar From 8819b3b1bb9ef8e9d81bbe86e2b3f18acf1e6b25 Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 31 Jul 2026 08:36:11 -0700 Subject: [PATCH 11/12] Stop death bounce-back from hijacking the drops The death drop protection added in 8d88d9a took the items out of the PlayerDeathEvent at NORMAL priority, dropped them by hand, and cleared the drops list. Any plugin that stores death drops - DeathChest, graves, keep-inventory - then ran on an empty event while the items lay on the ground, so with the default bounce-back: true, Border 4.7.0+ silently broke every such plugin. Leave the event alone: note the death spot at MONITOR, after those plugins have decided what happens to the items, and apply the bounce-back tracking to whatever the server then actually spawns, caught through ItemSpawnEvent in the same tick. If another plugin keeps the items, nothing spawns and there is nothing to track. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_013RPEWFAQcCivzQqB4L72Bx --- .../border/listeners/PlayerListener.java | 62 ++++++++++-- .../border/listeners/PlayerListenerTest.java | 96 +++++++++++++++++++ 2 files changed, 148 insertions(+), 10 deletions(-) diff --git a/src/main/java/world/bentobox/border/listeners/PlayerListener.java b/src/main/java/world/bentobox/border/listeners/PlayerListener.java index 1a98601..61ef3af 100644 --- a/src/main/java/world/bentobox/border/listeners/PlayerListener.java +++ b/src/main/java/world/bentobox/border/listeners/PlayerListener.java @@ -1,7 +1,9 @@ package world.bentobox.border.listeners; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -23,6 +25,7 @@ import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityDismountEvent; import org.bukkit.event.entity.EntityMountEvent; +import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerJoinEvent; @@ -71,10 +74,18 @@ public class PlayerListener implements Listener { private static final Vector XZ = new Vector(1, 0, 1); + /** How close to a death spot, in blocks, an item must spawn to count as a death drop. */ + private static final double DEATH_DROP_RADIUS_SQUARED = 5 * 5D; private final Border addon; private final Set inTeleport; private final BorderShower show; private final Map mountedPlayers = new HashMap<>(); + /** Death spots whose drops the server has yet to spawn. Entries last one tick. */ + private final List pendingDeathDrops = new ArrayList<>(); + + /** A death whose drops will spawn this tick, and the island they must stay on. */ + private record DeathDrops(Location location, Island island) { + } /** * Constructs a new PlayerListener. @@ -500,21 +511,52 @@ && isOn(event.getPlayer()) } /** - * Bounces items back to inside the barrier if dropped when a player dies + * Remembers where a player died so that the items the server is about to drop there can + * be bounced back inside the barrier. + *

+ * The drops list must not be touched here: taking the items out of the event and dropping + * them by hand breaks every plugin that stores death drops (death chests, graves, keep + * inventory). Instead this runs at MONITOR, after those plugins have decided what happens + * to the items, and only notes the death spot. Whatever the server then actually drops is + * picked up in {@link #onDeathDropSpawn(ItemSpawnEvent)} and tracked from there. * * @param event event */ - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + @EventHandler(priority = EventPriority.MONITOR) public void onPlayerDeath(PlayerDeathEvent event) { - if (addon.getSettings().isBounceBack() - && addon.inGameWorld(event.getPlayer().getWorld()) - && isOn(event.getPlayer())) { - Location loc = event.getPlayer().getLocation(); - addon.getIslands().getIslandAt(Objects.requireNonNull(loc)).ifPresent(is -> { - event.getDrops().forEach(item -> trackItem(event.getPlayer().getWorld().dropItemNaturally(loc, item), is)); - event.getDrops().clear(); // We handled them - }); + if (!addon.getSettings().isBounceBack() + || event.getDrops().isEmpty() + || !addon.inGameWorld(event.getPlayer().getWorld()) + || !isOn(event.getPlayer())) { + return; + } + Location loc = event.getPlayer().getLocation(); + addon.getIslands().getIslandAt(Objects.requireNonNull(loc)).ifPresent(is -> { + DeathDrops deathDrops = new DeathDrops(loc, is); + pendingDeathDrops.add(deathDrops); + // The server spawns the drops in this tick, straight after the event returns + Bukkit.getScheduler().runTask(addon.getPlugin(), () -> pendingDeathDrops.remove(deathDrops)); + }); + } + + /** + * Tracks death drops as the server spawns them, so they cannot leave the island's + * protection zone. Items spawning this tick near a recorded death spot are the drops the + * server kept after every plugin had its say on the death event. + * + * @param event event + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onDeathDropSpawn(ItemSpawnEvent event) { + if (pendingDeathDrops.isEmpty()) { + return; } + Location loc = event.getLocation(); + pendingDeathDrops.stream() + .filter(dd -> Objects.equals(loc.getWorld(), dd.location().getWorld())) + .filter(dd -> loc.distanceSquared(dd.location()) <= DEATH_DROP_RADIUS_SQUARED) + .findFirst() + .ifPresent(dd -> trackItem(event.getEntity(), dd.island())); } /** diff --git a/src/test/java/world/bentobox/border/listeners/PlayerListenerTest.java b/src/test/java/world/bentobox/border/listeners/PlayerListenerTest.java index ed3d562..5ee7391 100644 --- a/src/test/java/world/bentobox/border/listeners/PlayerListenerTest.java +++ b/src/test/java/world/bentobox/border/listeners/PlayerListenerTest.java @@ -1,5 +1,6 @@ package world.bentobox.border.listeners; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -11,6 +12,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -19,8 +21,12 @@ import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; +import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.entity.Vehicle; +import org.bukkit.event.entity.ItemSpawnEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.inventory.ItemStack; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; @@ -74,6 +80,16 @@ class PlayerListenerTest extends CommonTestSetup { private Vehicle vehicle; @Mock private GameModeAddon gma; + @Mock + private PlayerDeathEvent deathEvent; + @Mock + private ItemSpawnEvent itemSpawnEvent; + @Mock + private Item item; + @Mock + private ItemStack itemStack; + @Mock + private Location farAway; private MockedStatic mockedUser; @@ -464,4 +480,84 @@ void testOnProtectionRangeChange() { verify(show).showBorder(player, island); } + /** + * Test method for {@link world.bentobox.border.listeners.PlayerListener#onPlayerDeath(org.bukkit.event.entity.PlayerDeathEvent)}. + * The drops must be left in the event for death chest plugins and the server to handle. + */ + @Test + void testOnPlayerDeathLeavesDropsAlone() { + List drops = new ArrayList<>(); + drops.add(itemStack); + when(deathEvent.getPlayer()).thenReturn(player); + when(deathEvent.getDrops()).thenReturn(drops); + pl.onPlayerDeath(deathEvent); + assertEquals(1, drops.size()); + verify(world, never()).dropItemNaturally(any(Location.class), any(ItemStack.class)); + // The death spot is remembered, and forgotten again next tick + verify(sch).runTask(eq(plugin), any(Runnable.class)); + } + + /** + * Test method for {@link world.bentobox.border.listeners.PlayerListener#onDeathDropSpawn(org.bukkit.event.entity.ItemSpawnEvent)}. + * An item spawning at a fresh death spot is a death drop and gets the bounce-back tracking. + */ + @Test + void testDeathDropsAreTrackedWhenTheySpawn() { + List drops = new ArrayList<>(); + drops.add(itemStack); + when(deathEvent.getPlayer()).thenReturn(player); + when(deathEvent.getDrops()).thenReturn(drops); + pl.onPlayerDeath(deathEvent); + when(itemSpawnEvent.getLocation()).thenReturn(to); + when(itemSpawnEvent.getEntity()).thenReturn(item); + pl.onDeathDropSpawn(itemSpawnEvent); + verify(sch).runTaskTimer(eq(plugin), any(Runnable.class), eq(1L), eq(1L)); + } + + /** + * Test method for {@link world.bentobox.border.listeners.PlayerListener#onDeathDropSpawn(org.bukkit.event.entity.ItemSpawnEvent)}. + */ + @Test + void testItemSpawnWithNoRecentDeathIsIgnored() { + pl.onDeathDropSpawn(itemSpawnEvent); + verify(sch, never()).runTaskTimer(any(), any(Runnable.class), eq(1L), eq(1L)); + } + + /** + * Test method for {@link world.bentobox.border.listeners.PlayerListener#onDeathDropSpawn(org.bukkit.event.entity.ItemSpawnEvent)}. + */ + @Test + void testItemSpawnFarFromDeathIsNotTracked() { + List drops = new ArrayList<>(); + drops.add(itemStack); + when(deathEvent.getPlayer()).thenReturn(player); + when(deathEvent.getDrops()).thenReturn(drops); + pl.onPlayerDeath(deathEvent); + when(farAway.getWorld()).thenReturn(world); + when(farAway.distanceSquared(to)).thenReturn(100.0); + when(itemSpawnEvent.getLocation()).thenReturn(farAway); + pl.onDeathDropSpawn(itemSpawnEvent); + verify(sch, never()).runTaskTimer(any(), any(Runnable.class), eq(1L), eq(1L)); + } + + /** + * Test method for {@link world.bentobox.border.listeners.PlayerListener#onPlayerDeath(org.bukkit.event.entity.PlayerDeathEvent)}. + */ + @Test + void testOnPlayerDeathBounceBackDisabled() { + settings.setBounceBack(false); + pl.onPlayerDeath(deathEvent); + verify(sch, never()).runTask(any(), any(Runnable.class)); + } + + /** + * Test method for {@link world.bentobox.border.listeners.PlayerListener#onPlayerDeath(org.bukkit.event.entity.PlayerDeathEvent)}. + */ + @Test + void testOnPlayerDeathNoDrops() { + when(deathEvent.getDrops()).thenReturn(new ArrayList<>()); + pl.onPlayerDeath(deathEvent); + verify(sch, never()).runTask(any(), any(Runnable.class)); + } + } From 59d533687579d556d45773677def0812246dfc5d Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 31 Jul 2026 16:57:59 -0700 Subject: [PATCH 12/12] Update build.version from 4.8.4 to 4.8.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dfde37e..ca46997 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ ${build.version}-SNAPSHOT - 4.8.4 + 4.8.5 -LOCAL BentoBoxWorld_Border