feat(pocket-base): allow mobs through portals
Release / release (push) Successful in 3m0s
CI / build (push) Successful in 1m24s

This commit is contained in:
dmg
2026-08-24 13:10:53 -04:00
parent 29413d36b3
commit 8b3eaad253
5 changed files with 235 additions and 9 deletions
@@ -1,5 +1,6 @@
package games.dmg.spigotbase;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -8,20 +9,26 @@ import static org.mockito.Mockito.when;
import java.time.Instant;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Logger;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Player;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.BoundingBox;
import org.junit.jupiter.api.Test;
final class PocketBaseControllerTest {
@@ -230,6 +237,151 @@ final class PocketBaseControllerTest {
verify(event).setCancelled(true);
}
@Test
void activeEntranceTransportsMobsOnceButNotOtherEntities() {
UUID ownerId = UUID.randomUUID();
UUID worldId = UUID.randomUUID();
PocketPortalLocation portal = new PocketPortalLocation(
worldId, "world", -2, 64, 0, PocketPortalAxis.X
);
PocketBaseState state = new PocketBaseState(ownerId, 1, Optional.of(portal));
Server server = mock(Server.class);
World world = intactPortalWorld(worldId, portal);
Mob mob = mock(Mob.class);
Entity item = mock(Entity.class);
Location arrival = new Location(mock(World.class), 0.5, 65.0, 5.5);
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
when(mob.getUniqueId()).thenReturn(UUID.randomUUID());
when(item.getUniqueId()).thenReturn(UUID.randomUUID());
when(world.getNearbyEntities(any(BoundingBox.class), any()))
.thenReturn(List.of(mob, item));
when(server.getWorld(worldId)).thenReturn(world);
when(pocketBases.knownStates()).thenReturn(Map.of(ownerId, state));
when(pocketBases.pocketArrival(ownerId)).thenReturn(arrival);
PocketBaseController controller = controller(server, pocketBases);
controller.scanMobPortals();
controller.scanMobPortals();
verify(mob).teleport(arrival, PlayerTeleportEvent.TeleportCause.PLUGIN);
verify(item, never()).teleport(
any(Location.class),
any(PlayerTeleportEvent.TeleportCause.class)
);
}
@Test
void activeReturnPortalTransportsMobOutsideIntactEntrance() {
UUID ownerId = UUID.randomUUID();
UUID normalWorldId = UUID.randomUUID();
UUID pocketWorldId = UUID.randomUUID();
PocketPortalLocation entrance = new PocketPortalLocation(
normalWorldId, "world", -2, 64, 0, PocketPortalAxis.X
);
PocketPortalLocation returnPortal = new PocketPortalLocation(
pocketWorldId, "pocket", 8, 65, 8, PocketPortalAxis.Z
);
PocketBaseState state = new PocketBaseState(ownerId, 1, Optional.of(entrance));
Server server = mock(Server.class);
World normalWorld = intactPortalWorld(normalWorldId, entrance);
World pocketWorld = intactPortalWorld(pocketWorldId, returnPortal);
Mob mob = mock(Mob.class);
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
when(mob.getUniqueId()).thenReturn(UUID.randomUUID());
when(normalWorld.getNearbyEntities(any(BoundingBox.class), any()))
.thenReturn(List.of());
when(pocketWorld.getNearbyEntities(any(BoundingBox.class), any()))
.thenReturn(List.of(mob));
when(server.getWorld(normalWorldId)).thenReturn(normalWorld);
when(server.getWorld(pocketWorldId)).thenReturn(pocketWorld);
when(pocketBases.knownStates()).thenReturn(Map.of(ownerId, state));
when(pocketBases.state(ownerId)).thenReturn(state);
when(pocketBases.returnPortalIsIntact(ownerId)).thenReturn(true);
when(pocketBases.returnPortal(ownerId)).thenReturn(returnPortal);
PocketBaseController controller = controller(server, pocketBases);
controller.scanMobPortals();
verify(mob).teleport(
new Location(normalWorld, 0.0, 65.0, 1.5),
PlayerTeleportEvent.TeleportCause.PLUGIN
);
}
@Test
void mobReturnFallsBackToServerSpawnWithoutValidEntrance() {
UUID ownerId = UUID.randomUUID();
UUID pocketWorldId = UUID.randomUUID();
PocketPortalLocation returnPortal = new PocketPortalLocation(
pocketWorldId, "pocket", 8, 65, 8, PocketPortalAxis.Z
);
PocketBaseState state = new PocketBaseState(ownerId, 1, Optional.empty());
Server server = mock(Server.class);
World pocketWorld = intactPortalWorld(pocketWorldId, returnPortal);
World fallbackWorld = mock(World.class);
Location spawn = new Location(fallbackWorld, 10.5, 70.0, 10.5);
Mob mob = mock(Mob.class);
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
when(mob.getUniqueId()).thenReturn(UUID.randomUUID());
when(pocketWorld.getNearbyEntities(any(BoundingBox.class), any()))
.thenReturn(List.of(mob));
when(server.getWorld(pocketWorldId)).thenReturn(pocketWorld);
when(server.getWorlds()).thenReturn(List.of(fallbackWorld));
when(fallbackWorld.getSpawnLocation()).thenReturn(spawn);
when(pocketBases.knownStates()).thenReturn(Map.of(ownerId, state));
when(pocketBases.state(ownerId)).thenReturn(state);
when(pocketBases.returnPortalIsIntact(ownerId)).thenReturn(true);
when(pocketBases.returnPortal(ownerId)).thenReturn(returnPortal);
PocketBaseController controller = controller(server, pocketBases);
controller.scanMobPortals();
verify(mob).teleport(
spawn,
PlayerTeleportEvent.TeleportCause.PLUGIN
);
}
private static PocketBaseController controller(
Server server,
PocketBaseManager pocketBases
) {
PluginSettings settings = PluginSettings.from(Map.of());
return new PocketBaseController(
server,
mock(BaseStateManager.class),
new BaseBoundsService(settings),
pocketBases,
new PluginSettingsProvider(settings),
Logger.getAnonymousLogger()
);
}
private static World intactPortalWorld(
UUID worldId,
PocketPortalLocation portal
) {
World world = mock(World.class);
Set<BlockPosition> frame = new HashSet<>(PocketPortalGeometry.frameBlocks(portal));
when(world.getUID()).thenReturn(worldId);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenAnswer(invocation -> {
BlockPosition position = new BlockPosition(
invocation.getArgument(0), invocation.getArgument(1), invocation.getArgument(2)
);
return block(
world,
position.x(),
position.y(),
position.z(),
frame.contains(position) ? Material.DIAMOND_BLOCK : Material.AIR
);
});
return world;
}
private static ItemStack item(Material material) {
ItemStack item = mock(ItemStack.class);
when(item.getType()).thenReturn(material);