1 Commits
Author SHA1 Message Date
dmg 9692df9101 fix(flight): show boundary warning only while flying
Release / release (push) Successful in 4m20s
CI / build (push) Successful in 1m13s
2026-08-21 22:23:01 -04:00
3 changed files with 95 additions and 2 deletions
@@ -21,7 +21,7 @@ As a **player with Base I**, I want to unlock controlled flight around my base s
- [x] Flight II expands the vertical range to 100 blocks below and above base Y by default.
- [x] Flight III expands the vertical range to the world's minimum and maximum build heights.
- [x] A configurable five-block horizontal warning buffer extends beyond the current base radius.
- [x] Plugin-granted flight remains active in the warning buffer and displays prominent on-screen notice that the player is leaving the base.
- [x] Plugin-granted flight remains active in the warning buffer, and a prominent on-screen notice that the player is leaving the base appears only while the player is actively flying.
- [x] Passing beyond the warning buffer removes only flight granted by this plugin.
- [x] Flight is not granted outside the unlocked vertical range.
- [x] `/basesettings flight enable` enables unlocked flight idempotently, and `/basesettings flight disable` disables it idempotently.
@@ -112,7 +112,7 @@ final class BaseFlightController implements Runnable {
player.setAllowFlight(true);
grantedFlight.add(player.getUniqueId());
}
if (distanceSquared > (double) radius * radius) {
if (distanceSquared > (double) radius * radius && player.isFlying()) {
if (warned.add(player.getUniqueId())) {
player.sendTitle(
ChatColor.RED + "Leaving Your Base",
@@ -0,0 +1,93 @@
package games.dmg.spigotbase;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
final class BaseFlightControllerTest {
private final UUID playerId = UUID.randomUUID();
private final UUID worldId = UUID.randomUUID();
private final Server server = mock(Server.class);
private final BaseStateManager stateManager = mock(BaseStateManager.class);
private final Player player = mock(Player.class);
private final PlayerState state = PlayerState.newPlayer(playerId, "Alex")
.withGrassAndDirtProgress(250, 1)
.withBase(new BaseLocation(worldId, "world", 0, 64, 0, 0, 0), Instant.EPOCH)
.withFlightLevel(1, true);
private final BaseFlightController controller = new BaseFlightController(
server,
stateManager,
new SecondaryProgressionService(PluginSettings.from(Map.of())),
new BaseBoundsService(PluginSettings.from(Map.of())),
new PluginSettingsProvider(PluginSettings.from(Map.of()))
);
@BeforeEach
void setUp() {
World world = mock(World.class);
Location location = mock(Location.class);
PlayerInventory inventory = mock(PlayerInventory.class);
doReturn(List.of(player)).when(server).getOnlinePlayers();
when(player.getUniqueId()).thenReturn(playerId);
when(player.getName()).thenReturn("Alex");
when(player.getGameMode()).thenReturn(org.bukkit.GameMode.SURVIVAL);
when(player.getInventory()).thenReturn(inventory);
when(inventory.getStorageContents()).thenReturn(new ItemStack[0]);
when(stateManager.player(playerId, "Alex")).thenReturn(state);
when(player.getWorld()).thenReturn(world);
when(world.getUID()).thenReturn(worldId);
when(player.getLocation()).thenReturn(location);
when(location.getX()).thenReturn(12.5);
when(location.getZ()).thenReturn(0.5);
when(location.getBlockY()).thenReturn(64);
when(player.getAllowFlight()).thenReturn(true);
}
@Test
void walkingInWarningBufferDoesNotShowLeavingBaseWarning() {
when(player.isFlying()).thenReturn(false);
controller.run();
verify(player, never()).sendTitle(anyString(), anyString(), anyInt(), anyInt(), anyInt());
}
@Test
void flyingInWarningBufferShowsLeavingBaseWarning() {
when(player.isFlying()).thenReturn(true);
controller.run();
verify(player).sendTitle(anyString(), anyString(), anyInt(), anyInt(), anyInt());
}
@Test
void warningCanAppearAgainAfterPlayerStopsAndResumesFlyingInBuffer() {
when(player.isFlying()).thenReturn(true, false, true);
controller.run();
controller.run();
controller.run();
verify(player, times(2)).sendTitle(anyString(), anyString(), anyInt(), anyInt(), anyInt());
}
}