fix(pocket-base): clear fall distance after portal travel
This commit is contained in:
@@ -143,3 +143,9 @@ description: Chronological record of material decisions affecting the Spigot Bas
|
|||||||
- Reused safe player destinations and server-spawn fallback behavior while excluding items, projectiles, vehicles, and other non-living entities.
|
- Reused safe player destinations and server-spawn fallback behavior while excluding items, projectiles, vehicles, and other non-living entities.
|
||||||
- Applied the existing two-second portal cooldown to mobs to prevent immediate repeated transport.
|
- Applied the existing two-second portal cooldown to mobs to prevent immediate repeated transport.
|
||||||
- Verified the feature and Java 25 plugin artifact with `./gradlew clean check jar`.
|
- Verified the feature and Java 25 plugin artifact with `./gradlew clean check jar`.
|
||||||
|
|
||||||
|
## 2026-08-28 — Safe Pocket Base fall returns
|
||||||
|
|
||||||
|
- Cleared accumulated fall distance whenever portal or void-return travel teleports an entity, preventing carried-over fall damage at the destination.
|
||||||
|
- Added regression coverage for players falling out of a Pocket Base and returning to the server spawn.
|
||||||
|
- Verified the fix and plugin artifact with `./gradlew clean check jar`.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ As a **Pocket Base owner**, I want to connect my normal base to my Pocket Base w
|
|||||||
- [x] Breaking the active return frame disables return travel through it.
|
- [x] Breaking the active return frame disables return travel through it.
|
||||||
- [x] Return travel falls back to the server spawn when the owner has no valid normal-world entrance.
|
- [x] Return travel falls back to the server spawn when the owner has no valid normal-world entrance.
|
||||||
- [x] Falling below Y -64 performs the same safe return without void damage.
|
- [x] Falling below Y -64 performs the same safe return without void damage.
|
||||||
|
- [x] Portal and void-return travel clears accumulated fall distance so players do not take carried-over fall damage after landing at the destination.
|
||||||
- [x] Players who disconnect inside a Pocket Base remain there when they reconnect.
|
- [x] Players who disconnect inside a Pocket Base remain there when they reconnect.
|
||||||
- [x] Moving the normal base deactivates its entrance while preserving the Pocket Base world and its contents.
|
- [x] Moving the normal base deactivates its entrance while preserving the Pocket Base world and its contents.
|
||||||
- [x] Portal ownership and the active entrance location persist through restarts.
|
- [x] Portal ownership and the active entrance location persist through restarts.
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ final class PocketBaseController implements Listener, Runnable {
|
|||||||
|
|
||||||
private void teleport(Entity entity, Location destination) {
|
private void teleport(Entity entity, Location destination) {
|
||||||
cooldownUntil.put(entity.getUniqueId(), System.nanoTime() + PORTAL_COOLDOWN_NANOS);
|
cooldownUntil.put(entity.getUniqueId(), System.nanoTime() + PORTAL_COOLDOWN_NANOS);
|
||||||
|
entity.setFallDistance(0.0F);
|
||||||
entity.teleport(destination, PlayerTeleportEvent.TeleportCause.PLUGIN);
|
entity.teleport(destination, PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.event.block.Action;
|
import org.bukkit.event.block.Action;
|
||||||
import org.bukkit.event.block.BlockPlaceEvent;
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.bukkit.event.player.PlayerMoveEvent;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.util.BoundingBox;
|
import org.bukkit.util.BoundingBox;
|
||||||
@@ -310,6 +311,37 @@ final class PocketBaseControllerTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void voidReturnClearsAccumulatedPlayerFallDistance() {
|
||||||
|
UUID ownerId = UUID.randomUUID();
|
||||||
|
UUID pocketWorldId = UUID.randomUUID();
|
||||||
|
World pocketWorld = mock(World.class);
|
||||||
|
World fallbackWorld = mock(World.class);
|
||||||
|
Location destination = new Location(pocketWorld, 0.5, -65.0, 0.5);
|
||||||
|
Location spawn = new Location(fallbackWorld, 10.5, 70.0, 10.5);
|
||||||
|
Player player = mock(Player.class);
|
||||||
|
PlayerMoveEvent event = mock(PlayerMoveEvent.class);
|
||||||
|
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
|
||||||
|
Server server = mock(Server.class);
|
||||||
|
|
||||||
|
when(pocketWorld.getUID()).thenReturn(pocketWorldId);
|
||||||
|
when(player.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||||
|
when(event.getPlayer()).thenReturn(player);
|
||||||
|
when(event.getTo()).thenReturn(destination);
|
||||||
|
when(server.getWorlds()).thenReturn(List.of(fallbackWorld));
|
||||||
|
when(fallbackWorld.getSpawnLocation()).thenReturn(spawn);
|
||||||
|
when(pocketBases.ownerForPocketWorld(pocketWorldId)).thenReturn(Optional.of(ownerId));
|
||||||
|
when(pocketBases.state(ownerId)).thenReturn(
|
||||||
|
new PocketBaseState(ownerId, 1, Optional.empty())
|
||||||
|
);
|
||||||
|
PocketBaseController controller = controller(server, pocketBases);
|
||||||
|
|
||||||
|
controller.onMove(event);
|
||||||
|
|
||||||
|
verify(player).setFallDistance(0.0F);
|
||||||
|
verify(player).teleport(spawn, PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void mobReturnFallsBackToServerSpawnWithoutValidEntrance() {
|
void mobReturnFallsBackToServerSpawnWithoutValidEntrance() {
|
||||||
UUID ownerId = UUID.randomUUID();
|
UUID ownerId = UUID.randomUUID();
|
||||||
|
|||||||
Reference in New Issue
Block a user