439 lines
18 KiB
Java
439 lines
18 KiB
Java
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;
|
|
import static org.mockito.Mockito.verify;
|
|
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.PlayerMoveEvent;
|
|
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 {
|
|
@Test
|
|
void ownerCanActivateCompleteFrameInsideNormalBase() throws Exception {
|
|
UUID ownerId = UUID.randomUUID();
|
|
UUID worldId = UUID.randomUUID();
|
|
PocketPortalLocation portal = new PocketPortalLocation(
|
|
worldId, "world", -2, 64, 0, PocketPortalAxis.X
|
|
);
|
|
Set<BlockPosition> frame = new HashSet<>(PocketPortalGeometry.frameBlocks(portal));
|
|
Player owner = mock(Player.class);
|
|
World world = mock(World.class);
|
|
Block clicked = block(world, -2, 66, 0, Material.DIAMOND_BLOCK);
|
|
PlayerInteractEvent event = mock(PlayerInteractEvent.class);
|
|
ItemStack activator = item(Material.FLINT_AND_STEEL);
|
|
BaseStateManager baseStates = mock(BaseStateManager.class);
|
|
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
|
|
PluginSettings settings = PluginSettings.from(Map.of());
|
|
PlayerState ownerState = PlayerState.newPlayer(ownerId, "Owner")
|
|
.withAdministrativeLevels(4, 0, 0, 0, 0, false, false, true)
|
|
.withBase(new BaseLocation(worldId, "world", 0, 64, 0, 0, 0), Instant.EPOCH);
|
|
|
|
when(owner.getUniqueId()).thenReturn(ownerId);
|
|
when(owner.getName()).thenReturn("Owner");
|
|
when(event.getPlayer()).thenReturn(owner);
|
|
when(event.getAction()).thenReturn(Action.RIGHT_CLICK_BLOCK);
|
|
when(event.getClickedBlock()).thenReturn(clicked);
|
|
when(event.getItem()).thenReturn(activator);
|
|
when(world.getUID()).thenReturn(worldId);
|
|
when(world.getName()).thenReturn("world");
|
|
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
|
|
);
|
|
});
|
|
when(baseStates.player(ownerId, "Owner")).thenReturn(ownerState);
|
|
when(pocketBases.state(ownerId)).thenReturn(
|
|
new PocketBaseState(ownerId, 1, Optional.empty())
|
|
);
|
|
when(pocketBases.activateEntrance(ownerId, portal)).thenReturn(
|
|
new PocketBaseState(ownerId, 1, Optional.of(portal))
|
|
);
|
|
PocketBaseController controller = new PocketBaseController(
|
|
mock(Server.class),
|
|
baseStates,
|
|
new BaseBoundsService(settings),
|
|
pocketBases,
|
|
new PluginSettingsProvider(settings),
|
|
Logger.getAnonymousLogger()
|
|
);
|
|
|
|
controller.onActivate(event);
|
|
|
|
verify(pocketBases).activateEntrance(ownerId, portal);
|
|
verify(event).setCancelled(true);
|
|
}
|
|
|
|
@Test
|
|
void ownerCanActivateCompleteReturnFrameInsideOwnPocketBase() throws Exception {
|
|
UUID ownerId = UUID.randomUUID();
|
|
UUID worldId = UUID.randomUUID();
|
|
PocketPortalLocation portal = new PocketPortalLocation(
|
|
worldId, "pocket", 8, 65, 8, PocketPortalAxis.X
|
|
);
|
|
Set<BlockPosition> frame = new HashSet<>(PocketPortalGeometry.frameBlocks(portal));
|
|
Player owner = mock(Player.class);
|
|
World world = mock(World.class);
|
|
Block clicked = block(world, 8, 67, 8, Material.DIAMOND_BLOCK);
|
|
PlayerInteractEvent event = mock(PlayerInteractEvent.class);
|
|
ItemStack activator = item(Material.FLINT_AND_STEEL);
|
|
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
|
|
PluginSettings settings = PluginSettings.from(Map.of());
|
|
|
|
when(owner.getUniqueId()).thenReturn(ownerId);
|
|
when(event.getPlayer()).thenReturn(owner);
|
|
when(event.getAction()).thenReturn(Action.RIGHT_CLICK_BLOCK);
|
|
when(event.getClickedBlock()).thenReturn(clicked);
|
|
when(event.getItem()).thenReturn(activator);
|
|
when(world.getUID()).thenReturn(worldId);
|
|
when(world.getName()).thenReturn("pocket");
|
|
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
|
|
);
|
|
});
|
|
when(pocketBases.ownerForPocketWorld(worldId)).thenReturn(Optional.of(ownerId));
|
|
when(pocketBases.state(ownerId)).thenReturn(
|
|
new PocketBaseState(ownerId, 1, Optional.empty())
|
|
);
|
|
when(pocketBases.policy()).thenReturn(new PocketBasePolicy(settings));
|
|
PocketBaseController controller = new PocketBaseController(
|
|
mock(Server.class),
|
|
mock(BaseStateManager.class),
|
|
new BaseBoundsService(settings),
|
|
pocketBases,
|
|
new PluginSettingsProvider(settings),
|
|
Logger.getAnonymousLogger()
|
|
);
|
|
|
|
controller.onActivate(event);
|
|
|
|
verify(pocketBases).activateReturnPortal(ownerId, portal);
|
|
verify(event).setCancelled(true);
|
|
}
|
|
|
|
@Test
|
|
void returnFrameOutsideUnlockedPocketBoundaryIsRejected() throws Exception {
|
|
UUID ownerId = UUID.randomUUID();
|
|
UUID worldId = UUID.randomUUID();
|
|
PocketPortalLocation portal = new PocketPortalLocation(
|
|
worldId, "pocket", 30, 65, 8, PocketPortalAxis.X
|
|
);
|
|
Set<BlockPosition> frame = new HashSet<>(PocketPortalGeometry.frameBlocks(portal));
|
|
Player owner = mock(Player.class);
|
|
World world = mock(World.class);
|
|
Block clicked = block(world, 30, 67, 8, Material.DIAMOND_BLOCK);
|
|
PlayerInteractEvent event = mock(PlayerInteractEvent.class);
|
|
ItemStack activator = item(Material.FLINT_AND_STEEL);
|
|
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
|
|
PluginSettings settings = PluginSettings.from(Map.of());
|
|
|
|
when(owner.getUniqueId()).thenReturn(ownerId);
|
|
when(event.getPlayer()).thenReturn(owner);
|
|
when(event.getAction()).thenReturn(Action.RIGHT_CLICK_BLOCK);
|
|
when(event.getClickedBlock()).thenReturn(clicked);
|
|
when(event.getItem()).thenReturn(activator);
|
|
when(world.getUID()).thenReturn(worldId);
|
|
when(world.getName()).thenReturn("pocket");
|
|
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
|
|
);
|
|
});
|
|
when(pocketBases.ownerForPocketWorld(worldId)).thenReturn(Optional.of(ownerId));
|
|
when(pocketBases.state(ownerId)).thenReturn(
|
|
new PocketBaseState(ownerId, 1, Optional.empty())
|
|
);
|
|
when(pocketBases.policy()).thenReturn(new PocketBasePolicy(settings));
|
|
PocketBaseController controller = new PocketBaseController(
|
|
mock(Server.class),
|
|
mock(BaseStateManager.class),
|
|
new BaseBoundsService(settings),
|
|
pocketBases,
|
|
new PluginSettingsProvider(settings),
|
|
Logger.getAnonymousLogger()
|
|
);
|
|
|
|
controller.onActivate(event);
|
|
|
|
verify(pocketBases, never()).activateReturnPortal(ownerId, portal);
|
|
verify(event).setCancelled(true);
|
|
}
|
|
|
|
@Test
|
|
void placementOutsideUnlockedPocketBoundaryIsCancelled() {
|
|
UUID ownerId = UUID.randomUUID();
|
|
UUID worldId = UUID.randomUUID();
|
|
World world = mock(World.class);
|
|
Block placed = block(world, 32, 65, 0, Material.STONE);
|
|
Player player = mock(Player.class);
|
|
BlockPlaceEvent event = mock(BlockPlaceEvent.class);
|
|
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
|
|
PluginSettings settings = PluginSettings.from(Map.of());
|
|
|
|
when(world.getUID()).thenReturn(worldId);
|
|
when(event.getBlockPlaced()).thenReturn(placed);
|
|
when(event.getPlayer()).thenReturn(player);
|
|
when(pocketBases.ownerForPocketWorld(worldId)).thenReturn(Optional.of(ownerId));
|
|
when(pocketBases.state(ownerId)).thenReturn(
|
|
new PocketBaseState(ownerId, 1, Optional.empty())
|
|
);
|
|
when(pocketBases.policy()).thenReturn(new PocketBasePolicy(settings));
|
|
PocketBaseController controller = new PocketBaseController(
|
|
mock(Server.class),
|
|
mock(BaseStateManager.class),
|
|
new BaseBoundsService(settings),
|
|
pocketBases,
|
|
new PluginSettingsProvider(settings),
|
|
Logger.getAnonymousLogger()
|
|
);
|
|
|
|
controller.onPlace(event);
|
|
|
|
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 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
|
|
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);
|
|
return item;
|
|
}
|
|
|
|
private static Block block(
|
|
World world,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
Material material
|
|
) {
|
|
Block block = mock(Block.class);
|
|
when(block.getWorld()).thenReturn(world);
|
|
when(block.getX()).thenReturn(x);
|
|
when(block.getY()).thenReturn(y);
|
|
when(block.getZ()).thenReturn(z);
|
|
when(block.getType()).thenReturn(material);
|
|
return block;
|
|
}
|
|
}
|