From 54873f408506bd3ee0e19497ef66d29f1484c5b3 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Sun, 15 Feb 2026 22:23:25 +0100 Subject: [PATCH 1/2] fix: prevent delayed knockback teleport from overriding /back after death. Reported on Discord. --- .../com/eternalcode/combat/region/Region.java | 13 +++++++ .../com/eternalcode/combat/CombatPlugin.java | 2 +- .../fight/knockback/KnockbackService.java | 21 ++++++++-- .../region/RegionContainsWorldCheckTest.java | 38 +++++++++++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 eternalcombat-plugin/test/com/eternalcode/combat/region/RegionContainsWorldCheckTest.java diff --git a/eternalcombat-api/src/main/java/com/eternalcode/combat/region/Region.java b/eternalcombat-api/src/main/java/com/eternalcode/combat/region/Region.java index 95eb0e6b..6d2907c2 100644 --- a/eternalcombat-api/src/main/java/com/eternalcode/combat/region/Region.java +++ b/eternalcombat-api/src/main/java/com/eternalcode/combat/region/Region.java @@ -1,6 +1,7 @@ package com.eternalcode.combat.region; import org.bukkit.Location; +import org.bukkit.World; public interface Region { @@ -11,6 +12,18 @@ public interface Region { Location getMax(); default boolean contains(Location location) { + if (location == null) { + return false; + } + + Location min = this.getMin(); + World regionWorld = min.getWorld(); + World locationWorld = location.getWorld(); + + if (regionWorld == null || locationWorld == null || !regionWorld.equals(locationWorld)) { + return false; + } + return this.contains(location.getX(), location.getY(), location.getZ()); } diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java index 7872ac47..a500f635 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java @@ -139,7 +139,7 @@ public void onEnable() { this.regionProvider = bridgeService.getRegionProvider(); BorderService borderService = new BorderServiceImpl(scheduler, server, regionProvider, eventManager, () -> pluginConfig.border); - KnockbackService knockbackService = new KnockbackService(pluginConfig, scheduler, regionProvider); + KnockbackService knockbackService = new KnockbackService(pluginConfig, this.fightManager, scheduler, regionProvider); this.liteCommands = LiteBukkitFactory.builder(FALLBACK_PREFIX, this, server) .message(LiteBukkitMessages.PLAYER_NOT_FOUND, pluginConfig.messagesSettings.playerNotFound) diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java index 99c91d64..4a65e840 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java @@ -1,6 +1,7 @@ package com.eternalcode.combat.fight.knockback; import com.eternalcode.combat.config.implementation.PluginConfig; +import com.eternalcode.combat.fight.FightManager; import com.eternalcode.combat.region.Point; import com.eternalcode.combat.region.Region; import com.eternalcode.combat.region.RegionProvider; @@ -19,19 +20,27 @@ public final class KnockbackService { private final PluginConfig config; + private final FightManager fightManager; private final MinecraftScheduler scheduler; private final RegionProvider regionProvider; private final Map insideRegion = new HashMap<>(); - public KnockbackService(PluginConfig config, MinecraftScheduler scheduler, RegionProvider regionProvider) { + public KnockbackService(PluginConfig config, FightManager fightManager, MinecraftScheduler scheduler, RegionProvider regionProvider) { this.config = config; + this.fightManager = fightManager; this.scheduler = scheduler; this.regionProvider = regionProvider; } public void knockbackLater(Region region, Player player, Duration duration) { - this.scheduler.runLater(() -> this.knockback(region, player), duration); + this.scheduler.runLater(() -> { + if (!this.fightManager.isInCombat(player.getUniqueId()) || player.isDead()) { + return; + } + + this.knockback(region, player); + }, duration); } public void forceKnockbackLater(Player player, Region region) { @@ -43,7 +52,13 @@ public void forceKnockbackLater(Player player, Region region) { scheduler.runLater( player.getLocation(), () -> { - insideRegion.remove(player.getUniqueId()); + UUID uniqueId = player.getUniqueId(); + insideRegion.remove(uniqueId); + + if (!this.fightManager.isInCombat(uniqueId) || player.isDead()) { + return; + } + Location playerLocation = player.getLocation(); if (!region.contains(playerLocation) && !regionProvider.isInRegion(playerLocation)) { return; diff --git a/eternalcombat-plugin/test/com/eternalcode/combat/region/RegionContainsWorldCheckTest.java b/eternalcombat-plugin/test/com/eternalcode/combat/region/RegionContainsWorldCheckTest.java new file mode 100644 index 00000000..7d83c293 --- /dev/null +++ b/eternalcombat-plugin/test/com/eternalcode/combat/region/RegionContainsWorldCheckTest.java @@ -0,0 +1,38 @@ +package com.eternalcode.combat.region; + +import org.bukkit.Location; +import org.bukkit.World; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; + +class RegionContainsWorldCheckTest { + + @Test + void shouldNotContainLocationFromDifferentWorld() { + World regionWorld = mock(World.class); + World otherWorld = mock(World.class); + + Region region = new Region() { + @Override + public Point getCenter() { + return new Point(regionWorld, 5.0, 5.0); + } + + @Override + public Location getMin() { + return new Location(regionWorld, 0.0, 0.0, 0.0); + } + + @Override + public Location getMax() { + return new Location(regionWorld, 10.0, 10.0, 10.0); + } + }; + + Location locationInOtherWorld = new Location(otherWorld, 5.0, 5.0, 5.0); + + assertFalse(region.contains(locationInOtherWorld)); + } +} From 92b16f5bf451cf95d6ad852c59326ffedaf34f24 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Sun, 15 Feb 2026 22:50:33 +0100 Subject: [PATCH 2/2] Fix issues reported by gemini code review. --- .../com/eternalcode/combat/CombatPlugin.java | 2 +- .../fight/knockback/KnockbackService.java | 24 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java index a500f635..2a08b9c2 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java @@ -139,7 +139,7 @@ public void onEnable() { this.regionProvider = bridgeService.getRegionProvider(); BorderService borderService = new BorderServiceImpl(scheduler, server, regionProvider, eventManager, () -> pluginConfig.border); - KnockbackService knockbackService = new KnockbackService(pluginConfig, this.fightManager, scheduler, regionProvider); + KnockbackService knockbackService = new KnockbackService(pluginConfig, this.fightManager, scheduler, regionProvider, server); this.liteCommands = LiteBukkitFactory.builder(FALLBACK_PREFIX, this, server) .message(LiteBukkitMessages.PLAYER_NOT_FOUND, pluginConfig.messagesSettings.playerNotFound) diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java index 4a65e840..cb5c3af2 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java @@ -13,6 +13,7 @@ import java.util.Optional; import java.util.UUID; import org.bukkit.Location; +import org.bukkit.Server; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.util.Vector; @@ -23,23 +24,33 @@ public final class KnockbackService { private final FightManager fightManager; private final MinecraftScheduler scheduler; private final RegionProvider regionProvider; + private final Server server; private final Map insideRegion = new HashMap<>(); - public KnockbackService(PluginConfig config, FightManager fightManager, MinecraftScheduler scheduler, RegionProvider regionProvider) { + public KnockbackService( + PluginConfig config, + FightManager fightManager, + MinecraftScheduler scheduler, + RegionProvider regionProvider, + Server server + ) { this.config = config; this.fightManager = fightManager; this.scheduler = scheduler; this.regionProvider = regionProvider; + this.server = server; } public void knockbackLater(Region region, Player player, Duration duration) { + UUID uniqueId = player.getUniqueId(); this.scheduler.runLater(() -> { - if (!this.fightManager.isInCombat(player.getUniqueId()) || player.isDead()) { + Player onlinePlayer = this.server.getPlayer(uniqueId); + if (onlinePlayer == null || !this.fightManager.isInCombat(uniqueId) || onlinePlayer.isDead()) { return; } - this.knockback(region, player); + this.knockback(region, onlinePlayer); }, duration); } @@ -55,11 +66,12 @@ public void forceKnockbackLater(Player player, Region region) { UUID uniqueId = player.getUniqueId(); insideRegion.remove(uniqueId); - if (!this.fightManager.isInCombat(uniqueId) || player.isDead()) { + Player onlinePlayer = this.server.getPlayer(uniqueId); + if (onlinePlayer == null || !this.fightManager.isInCombat(uniqueId) || onlinePlayer.isDead()) { return; } - Location playerLocation = player.getLocation(); + Location playerLocation = onlinePlayer.getLocation(); if (!region.contains(playerLocation) && !regionProvider.isInRegion(playerLocation)) { return; } @@ -67,7 +79,7 @@ public void forceKnockbackLater(Player player, Region region) { Location location = generate(playerLocation, Point2D.from(region.getMin()), Point2D.from(region.getMax())); - PaperLib.teleportAsync(player, location, TeleportCause.PLUGIN); + PaperLib.teleportAsync(onlinePlayer, location, TeleportCause.PLUGIN); }, this.config.knockback.forceDelay); }