Skip to content

Commit dfe5c2a

Browse files
Copilottastybento
andcommitted
Fix players falling into void on new island creation by adding safe-spot checking to homeTeleportAsync(Island, User, boolean)
The new homeTeleportAsync(Island, User, boolean) method introduced in 3.11.2 directly teleported players to island.getHome("") without checking if the location was safe. For addons like OneBlock that have no starter blueprint, this could place players inside a block or in the void. Now the method uses isSafeLocationAsync() to verify the location first, and falls back to SafeSpotTeleport if the location is unsafe, matching the safety behavior of the older homeTeleportAsync(World, Player, boolean). Agent-Logs-Url: https://github.com/BentoBoxWorld/BentoBox/sessions/575ad09c-95f5-40e0-8f1f-1bd8c8dfacd6 Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com>
1 parent bf9b460 commit dfe5c2a

File tree

2 files changed

+99
-12
lines changed

2 files changed

+99
-12
lines changed

src/main/java/world/bentobox/bentobox/managers/IslandsManager.java

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,17 +1885,36 @@ public CompletableFuture<Void> homeTeleportAsync(Island island, User user, boole
18851885
Location loc = island.getHome("");
18861886
user.sendMessage("commands.island.go.teleport");
18871887
goingHome.add(user.getUniqueId());
1888-
readyPlayer(user.getPlayer());
1889-
return Util.teleportAsync(Objects.requireNonNull(user.getPlayer()), loc).thenAccept(b -> {
1890-
// Only run the commands if the player is successfully teleported
1891-
if (b != null && b) {
1892-
teleported(island.getWorld(), user, "", newIsland, island);
1893-
this.setPrimaryIsland(user.getUniqueId(), island);
1888+
Player player = Objects.requireNonNull(user.getPlayer());
1889+
readyPlayer(player);
1890+
CompletableFuture<Void> result = new CompletableFuture<>();
1891+
isSafeLocationAsync(loc).thenAccept(safe -> {
1892+
if (Boolean.TRUE.equals(safe)) {
1893+
Util.teleportAsync(player, loc).thenAccept(b -> {
1894+
if (b != null && b) {
1895+
teleported(island.getWorld(), user, "", newIsland, island);
1896+
this.setPrimaryIsland(user.getUniqueId(), island);
1897+
} else {
1898+
goingHome.remove(user.getUniqueId());
1899+
}
1900+
result.complete(null);
1901+
});
18941902
} else {
1895-
// Remove from mid-teleport set
1896-
goingHome.remove(user.getUniqueId());
1903+
// Location is not safe, use SafeSpotTeleport to find a safe spot
1904+
new SafeSpotTeleport.Builder(plugin).entity(player).island(island)
1905+
.thenRun(() -> {
1906+
teleported(island.getWorld(), user, "", newIsland, island);
1907+
this.setPrimaryIsland(user.getUniqueId(), island);
1908+
})
1909+
.ifFail(() -> {
1910+
plugin.logError(user.getName()
1911+
+ " could not be teleported to home on island "
1912+
+ island.getCenter());
1913+
goingHome.remove(user.getUniqueId());
1914+
}).buildFuture().thenAccept(b -> result.complete(null));
18971915
}
18981916
});
1917+
return result;
18991918
}
19001919

19011920
}

src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,6 +1504,29 @@ void testsetMaxHomes() {
15041504
verify(island).setMaxHomes(40);
15051505
}
15061506

1507+
/**
1508+
* Helper to set up a mock home location with safe blocks for homeTeleportAsync tests.
1509+
* Returns the mocked home Location with block/chunk/world configured.
1510+
*/
1511+
private Location setupSafeHomeLoc() {
1512+
Location homeLoc = mock(Location.class);
1513+
when(homeLoc.getWorld()).thenReturn(world);
1514+
Block homeBlock = mock(Block.class);
1515+
Block homeGround = mock(Block.class);
1516+
Block homeSpace2 = mock(Block.class);
1517+
when(homeLoc.getBlock()).thenReturn(homeBlock);
1518+
when(homeBlock.getRelative(BlockFace.DOWN)).thenReturn(homeGround);
1519+
when(homeBlock.getRelative(BlockFace.UP)).thenReturn(homeSpace2);
1520+
// Safe location: solid ground, air above
1521+
when(homeGround.getType()).thenReturn(Material.STONE);
1522+
when(homeBlock.getType()).thenReturn(Material.AIR);
1523+
when(homeSpace2.getType()).thenReturn(Material.AIR);
1524+
// Mock chunk loading
1525+
Chunk homeChunk = mock(Chunk.class);
1526+
mockedUtil.when(() -> Util.getChunkAtAsync(homeLoc)).thenReturn(CompletableFuture.completedFuture(homeChunk));
1527+
return homeLoc;
1528+
}
1529+
15071530
/**
15081531
* Test method for
15091532
* {@link world.bentobox.bentobox.managers.IslandsManager#homeTeleportAsync(Island, User)}.
@@ -1512,7 +1535,7 @@ void testsetMaxHomes() {
15121535
void testHomeTeleportAsyncIslandUser() throws Exception {
15131536
// Setup
15141537
Island island = mock(Island.class);
1515-
Location homeLoc = mock(Location.class);
1538+
Location homeLoc = setupSafeHomeLoc();
15161539
when(island.getHome("")).thenReturn(homeLoc);
15171540
when(island.getWorld()).thenReturn(world);
15181541
when(user.getPlayer()).thenReturn(player);
@@ -1547,7 +1570,7 @@ void testHomeTeleportAsyncIslandUser() throws Exception {
15471570
void testHomeTeleportAsyncIslandUserBooleanWithDefaultHome() throws Exception {
15481571
// Setup
15491572
Island island = mock(Island.class);
1550-
Location homeLoc = mock(Location.class);
1573+
Location homeLoc = setupSafeHomeLoc();
15511574
when(island.getHome("")).thenReturn(homeLoc);
15521575
when(island.getWorld()).thenReturn(world);
15531576
when(user.getPlayer()).thenReturn(player);
@@ -1584,7 +1607,7 @@ void testHomeTeleportAsyncIslandUserBooleanWithDefaultHome() throws Exception {
15841607
void testHomeTeleportAsyncIslandUserBooleanNewIsland() throws Exception {
15851608
// Setup
15861609
Island island = mock(Island.class);
1587-
Location homeLoc = mock(Location.class);
1610+
Location homeLoc = setupSafeHomeLoc();
15881611
when(island.getHome("")).thenReturn(homeLoc);
15891612
when(island.getWorld()).thenReturn(world);
15901613
when(user.getPlayer()).thenReturn(player);
@@ -1621,7 +1644,7 @@ void testHomeTeleportAsyncIslandUserBooleanNewIsland() throws Exception {
16211644
void testHomeTeleportAsyncIslandUserBooleanFailedTeleport() throws Exception {
16221645
// Setup
16231646
Island island = mock(Island.class);
1624-
Location homeLoc = mock(Location.class);
1647+
Location homeLoc = setupSafeHomeLoc();
16251648
when(island.getHome("")).thenReturn(homeLoc);
16261649
when(island.getWorld()).thenReturn(world);
16271650
when(user.getPlayer()).thenReturn(player);
@@ -1646,4 +1669,49 @@ void testHomeTeleportAsyncIslandUserBooleanFailedTeleport() throws Exception {
16461669
verify(user).sendMessage("commands.island.go.teleport");
16471670
verify(island).getHome("");
16481671
}
1672+
1673+
/**
1674+
* Test method for
1675+
* {@link world.bentobox.bentobox.managers.IslandsManager#homeTeleportAsync(Island, User, boolean)}.
1676+
* Test with unsafe home location - should use SafeSpotTeleport fallback instead of direct teleport.
1677+
*/
1678+
@Test
1679+
void testHomeTeleportAsyncIslandUserBooleanUnsafeLocation() throws Exception {
1680+
// Setup
1681+
Island island = mock(Island.class);
1682+
Location homeLoc = mock(Location.class);
1683+
when(homeLoc.getWorld()).thenReturn(world);
1684+
Block homeBlock = mock(Block.class);
1685+
Block homeGround = mock(Block.class);
1686+
Block homeSpace2 = mock(Block.class);
1687+
when(homeLoc.getBlock()).thenReturn(homeBlock);
1688+
when(homeBlock.getRelative(BlockFace.DOWN)).thenReturn(homeGround);
1689+
when(homeBlock.getRelative(BlockFace.UP)).thenReturn(homeSpace2);
1690+
// Unsafe location: no solid ground (all AIR)
1691+
when(homeGround.getType()).thenReturn(Material.AIR);
1692+
when(homeBlock.getType()).thenReturn(Material.AIR);
1693+
when(homeSpace2.getType()).thenReturn(Material.AIR);
1694+
// Mock chunk loading
1695+
Chunk homeChunk = mock(Chunk.class);
1696+
mockedUtil.when(() -> Util.getChunkAtAsync(homeLoc)).thenReturn(CompletableFuture.completedFuture(homeChunk));
1697+
1698+
when(island.getHome("")).thenReturn(homeLoc);
1699+
when(island.getWorld()).thenReturn(world);
1700+
when(island.getProtectionCenter()).thenReturn(location);
1701+
when(user.getPlayer()).thenReturn(player);
1702+
when(user.getUniqueId()).thenReturn(uuid);
1703+
1704+
// Mock player methods called by readyPlayer
1705+
when(player.isInsideVehicle()).thenReturn(false);
1706+
1707+
// Test
1708+
IslandsManager localIM = new IslandsManager(plugin);
1709+
// homeTeleportAsync should NOT call Util.teleportAsync directly for unsafe locations
1710+
// It should fall back to SafeSpotTeleport
1711+
localIM.homeTeleportAsync(island, user, true);
1712+
1713+
// Verify that direct teleportAsync was NOT called (SafeSpotTeleport handles it instead)
1714+
mockedUtil.verify(() -> Util.teleportAsync(eq(player), eq(homeLoc)), never());
1715+
verify(user).sendMessage("commands.island.go.teleport");
1716+
}
16491717
}

0 commit comments

Comments
 (0)