Added ban enforcement class and test class.

This prevents players from entering islands when they are banned. In the
future, it should also be extended to enable island locking.

Still needs a bit of work around Ops and bypass perms.
This commit is contained in:
Tastybento 2018-04-29 18:48:38 -07:00
parent 76711f5d09
commit 3cbcf52319
8 changed files with 530 additions and 9 deletions

View File

@ -164,7 +164,8 @@ commands:
cannot-ban: "&cThat player cannot be banned."
cannot-ban-member: "&cKick the team member first, then ban."
player-already-banned: "&cPlayer is already banned"
you-are-banned: "&b[owner]&c banned you from their island!"
owner-banned-you: "&b[owner]&c banned you from their island!"
you-are-banned: "&bYou are banned from this island!"
unban:
description: "unban a player from your island"
parameters: "<player>"

View File

@ -9,6 +9,7 @@ import us.tastybento.bskyblock.commands.AdminCommand;
import us.tastybento.bskyblock.commands.IslandCommand;
import us.tastybento.bskyblock.database.BSBDbSetup;
import us.tastybento.bskyblock.generators.IslandWorld;
import us.tastybento.bskyblock.listeners.IslandBanEnforcer;
import us.tastybento.bskyblock.listeners.JoinLeaveListener;
import us.tastybento.bskyblock.listeners.NetherPortals;
import us.tastybento.bskyblock.listeners.ObsidianToLava;
@ -149,6 +150,8 @@ public class BSkyBlock extends JavaPlugin {
manager.registerEvents(new NetherPortals(this), this);
// Obsidian to lava helper
manager.registerEvents(new ObsidianToLava(this), this);
// Island ban enforcer
manager.registerEvents(new IslandBanEnforcer(this), this);
}
@Override

View File

@ -10,6 +10,7 @@ import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@ -166,8 +167,21 @@ public class User {
return player != null && player.isOnline();
}
/**
* Checks if user is Op
* @return true if user is Op
*/
public boolean isOp() {
return sender.isOp();
if (sender != null) {
return sender.isOp();
}
if (playerUUID != null) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerUUID);
if (offlinePlayer != null) {
return offlinePlayer.isOp();
}
}
return false;
}
/**

View File

@ -64,10 +64,10 @@ public class IslandBanCommand extends CompositeCommand {
if (getIslands().getIsland(playerUUID).isBanned(targetUUID)) {
user.sendMessage("commands.island.ban.player-already-banned");
return false;
}
}
User target = User.getInstance(targetUUID);
// Cannot ban ops
if (!target.isPlayer() || target.isOp()) {
if (target.isOp()) {
user.sendMessage("commands.island.ban.cannot-ban");
return false;
}
@ -78,7 +78,7 @@ public class IslandBanCommand extends CompositeCommand {
private boolean ban(User user, User targetUser) {
if (getIslands().getIsland(user.getUniqueId()).addToBanList(targetUser.getUniqueId())) {
user.sendMessage("general.success");
targetUser.sendMessage("commands.island.ban.you-are-banned", "[owner]", user.getName());
targetUser.sendMessage("commands.island.ban.owner-banned-you", "[owner]", user.getName());
if (targetUser.isOnline() && getPlayers().hasIsland(targetUser.getUniqueId())) {
getIslands().homeTeleport(targetUser.getPlayer());
}

View File

@ -0,0 +1,136 @@
/**
*
*/
package us.tastybento.bskyblock.listeners;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.vehicle.VehicleMoveEvent;
import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.api.user.User;
import us.tastybento.bskyblock.managers.IslandsManager;
/**
* Enforces island bans. Checks for teleporting, entry via flying and due to logging in
* @author tastybento
*
*/
public class IslandBanEnforcer implements Listener {
private IslandsManager im;
private Set<UUID> inTeleport;
/**
* Enforces island bans
* @param plugin
*/
public IslandBanEnforcer(BSkyBlock plugin) {
this.im = plugin.getIslands();
inTeleport = new HashSet<>();
}
// Teleport check
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent e) {
// Ignore players who are being ejected
if (inTeleport.contains(e.getPlayer().getUniqueId())) {
// Remove them
inTeleport.remove(e.getPlayer().getUniqueId());
return;
}
e.setCancelled(checkAndNotify(e.getPlayer(), e.getTo()));
// Check from - just in case the player is inside the island
if (check(e.getPlayer(), e.getFrom())) {
eject(e.getPlayer());
}
}
// Movement check
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent e) {
// Ignore only vertical movement
if (e.getFrom().getBlockX() - e.getTo().getBlockX() == 0 && e.getFrom().getBlockZ() - e.getTo().getBlockZ() == 0) {
return;
}
e.setCancelled(checkAndNotify(e.getPlayer(), e.getTo()));
// Check from - just in case the player is inside the island
if (check(e.getPlayer(), e.getFrom())) {
eject(e.getPlayer());
}
}
// Vehicle move check
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onVehicleMove(VehicleMoveEvent e) {
// Ignore only vertical movement
if (e.getFrom().getBlockX() - e.getTo().getBlockX() == 0 && e.getFrom().getBlockZ() - e.getTo().getBlockZ() == 0) {
return;
}
// For each Player in the vehicle
e.getVehicle().getPassengers().stream().filter(en -> en instanceof Player).map(en -> (Player)en).forEach(p -> {
if (checkAndNotify(p, e.getTo())) {
p.leaveVehicle();
p.teleport(e.getFrom());
eject(p);
}
});
}
// Login check
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerLogin(PlayerJoinEvent e) {
if (checkAndNotify(e.getPlayer(), e.getPlayer().getLocation())) {
eject(e.getPlayer());
}
}
/**
* Check if a player is banned from this location
* @param player - player
* @param loc - location to check
* @return true if banned
*/
private boolean check(Player player, Location loc) {
// See if player is banned
return im.getProtectedIslandAt(loc).map(is -> is.isBanned(player.getUniqueId())).orElse(false);
}
/**
* Checks if a player is banned from this location and notifies them if so
* @param player - player
* @param loc - location to check
* @return true if banned
*/
private boolean checkAndNotify(Player player, Location loc) {
if (check(player, loc)) {
User.getInstance(player).notify("commands.island.ban.you-are-banned");
return true;
}
return false;
}
/**
* Sends player home
* @param player
*/
private void eject(Player player) {
// Teleport player to their home
inTeleport.add(player.getUniqueId());
if (im.hasIsland(player.getUniqueId())) {
im.homeTeleport(player);
} // else, TODO: teleport somewhere else?
}
}

View File

@ -238,7 +238,7 @@ public class IslandBanCommandTest {
assertTrue(ibc.execute(user, Arrays.asList("bill")));
Mockito.verify(user).sendMessage("general.success");
Mockito.verify(targetUser).sendMessage("commands.island.ban.you-are-banned", "[owner]", user.getName());
Mockito.verify(targetUser).sendMessage("commands.island.ban.owner-banned-you", "[owner]", user.getName());
}
@Test
@ -259,7 +259,7 @@ public class IslandBanCommandTest {
assertTrue(ibc.execute(user, Arrays.asList("bill")));
Mockito.verify(user).sendMessage("general.success");
Mockito.verify(targetUser).sendMessage("commands.island.ban.you-are-banned", "[owner]", user.getName());
Mockito.verify(targetUser).sendMessage("commands.island.ban.owner-banned-you", "[owner]", user.getName());
}
@Test
@ -280,7 +280,7 @@ public class IslandBanCommandTest {
assertFalse(ibc.execute(user, Arrays.asList("bill")));
Mockito.verify(user, Mockito.never()).sendMessage("general.success");
Mockito.verify(targetUser, Mockito.never()).sendMessage("commands.island.ban.you-are-banned", "[owner]", user.getName());
Mockito.verify(targetUser, Mockito.never()).sendMessage("commands.island.ban.owner-banned-you", "[owner]", user.getName());
}
@Test

View File

@ -0,0 +1,367 @@
/**
*
*/
package us.tastybento.bskyblock.listeners;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.vehicle.VehicleMoveEvent;
import org.bukkit.scheduler.BukkitScheduler;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.Settings;
import us.tastybento.bskyblock.api.user.Notifier;
import us.tastybento.bskyblock.api.user.User;
import us.tastybento.bskyblock.database.objects.Island;
import us.tastybento.bskyblock.managers.IslandsManager;
import us.tastybento.bskyblock.managers.LocalesManager;
import us.tastybento.bskyblock.managers.PlayersManager;
/**
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class })
public class IslandBanEnforcerTest {
private static final Integer PROTECTION_RANGE = 200;
private static final Integer X = 600;
private static final Integer Y = 120;
private static final Integer Z = 10000;
private BSkyBlock plugin;
private UUID uuid;
private User user;
private Settings s;
private IslandsManager im;
private PlayersManager pm;
private Island island;
private World world;
private IslandBanEnforcer ibe;
private Location loc;
private Location outside;
private Location inside;
private Notifier notifier;
private Location inside2;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
// Set up plugin
plugin = mock(BSkyBlock.class);
Whitebox.setInternalState(BSkyBlock.class, "instance", plugin);
// World
world = mock(World.class);
// Settings
s = mock(Settings.class);
when(s.getResetWait()).thenReturn(0L);
when(s.getResetLimit()).thenReturn(3);
when(plugin.getSettings()).thenReturn(s);
// Player
Player p = mock(Player.class);
// Sometimes use Mockito.withSettings().verboseLogging()
user = mock(User.class);
User.setPlugin(plugin);
when(user.isOp()).thenReturn(false);
uuid = UUID.randomUUID();
when(user.getUniqueId()).thenReturn(uuid);
when(user.getPlayer()).thenReturn(p);
when(user.getName()).thenReturn("tastybento");
// No island for player to begin with (set it later in the tests)
im = mock(IslandsManager.class);
when(im.hasIsland(Mockito.eq(uuid))).thenReturn(false);
when(im.isOwner(Mockito.eq(uuid))).thenReturn(false);
when(plugin.getIslands()).thenReturn(im);
// Has team
pm = mock(PlayersManager.class);
when(pm.inTeam(Mockito.eq(uuid))).thenReturn(true);
when(plugin.getPlayers()).thenReturn(pm);
// Server & Scheduler
BukkitScheduler sch = mock(BukkitScheduler.class);
PowerMockito.mockStatic(Bukkit.class);
when(Bukkit.getScheduler()).thenReturn(sch);
// Locales
LocalesManager lm = mock(LocalesManager.class);
when(plugin.getLocalesManager()).thenReturn(lm);
when(lm.get(any(), any())).thenReturn("mock translation");
// Notifier
notifier = mock(Notifier.class);
when(plugin.getNotifier()).thenReturn(notifier);
// Island Banned list initialization
island = mock(Island.class);
when(island.getBanned()).thenReturn(new HashSet<>());
when(island.isBanned(Mockito.any())).thenReturn(false);
loc = mock(Location.class);
when(loc.getWorld()).thenReturn(world);
when(loc.getBlockX()).thenReturn(X);
when(loc.getBlockY()).thenReturn(Y);
when(loc.getBlockZ()).thenReturn(Z);
when(island.getCenter()).thenReturn(loc);
when(island.getProtectionRange()).thenReturn(PROTECTION_RANGE);
when(im.getIsland(Mockito.any(UUID.class))).thenReturn(island);
ibe = new IslandBanEnforcer(plugin);
// Common from to's
outside = mock(Location.class);
when(outside.getWorld()).thenReturn(world);
when(outside.getBlockX()).thenReturn(X + PROTECTION_RANGE + 1);
when(outside.getBlockY()).thenReturn(Y);
when(outside.getBlockZ()).thenReturn(Z);
inside = mock(Location.class);
when(inside.getWorld()).thenReturn(world);
when(inside.getBlockX()).thenReturn(X + PROTECTION_RANGE - 1);
when(inside.getBlockY()).thenReturn(Y);
when(inside.getBlockZ()).thenReturn(Z);
inside2 = mock(Location.class);
when(inside.getWorld()).thenReturn(world);
when(inside.getBlockX()).thenReturn(X + PROTECTION_RANGE - 2);
when(inside.getBlockY()).thenReturn(Y);
when(inside.getBlockZ()).thenReturn(Z);
Optional<Island> opIsland = Optional.ofNullable(island);
when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland);
when(im.getProtectedIslandAt(Mockito.eq(inside2))).thenReturn(opIsland);
when(im.getProtectedIslandAt(Mockito.eq(outside))).thenReturn(Optional.empty());
}
@Test
public void testTeleportToNotBannedIsland() {
// Setup location outside island, one inside banned island
// Make player
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(uuid);
// Simulate a teleport into an island
PlayerTeleportEvent e = new PlayerTeleportEvent(player, outside, inside);
// Pass to event listener
ibe.onPlayerTeleport(e);
// Should not be cancelled
assertFalse(e.isCancelled());
// User should see no message from this class
Mockito.verify(notifier, Mockito.never());
}
@Test
public void testTeleportToBannedIsland() {
// Make player
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(uuid);
// Add player to the ban list
when(island.isBanned(Mockito.eq(uuid))).thenReturn(true);
// Simulate a teleport into an island
PlayerTeleportEvent e = new PlayerTeleportEvent(player, outside, inside);
// Pass to event listener
ibe.onPlayerTeleport(e);
// Should be cancelled
assertTrue(e.isCancelled());
// Player should see a message
Mockito.verify(notifier);
}
@Test
public void testLoginToBannedIsland() {
// Make player
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(uuid);
// Give player an island
when(im.hasIsland(uuid)).thenReturn(true);
// Place the player on the island
when(player.getLocation()).thenReturn(inside);
// Add player to the ban list
when(island.isBanned(Mockito.eq(uuid))).thenReturn(true);
// Log them in
ibe.onPlayerLogin(new PlayerJoinEvent(player, "join message"));
// User should see a message
Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString());
// User should be teleported somewhere
Mockito.verify(im).homeTeleport(Mockito.eq(player));
// Call teleport event
PlayerTeleportEvent e = new PlayerTeleportEvent(player, inside, outside);
// Pass to event listener
ibe.onPlayerTeleport(e);
// Should not be cancelled
assertFalse(e.isCancelled());
}
@Test
public void testVerticalMoveOnly() {
// Move vertically only
Location from = mock(Location.class);
when(from.getWorld()).thenReturn(world);
when(from.getBlockX()).thenReturn(X);
when(from.getBlockY()).thenReturn(50);
when(from.getBlockZ()).thenReturn(Z);
Location to = mock(Location.class);
when(to.getWorld()).thenReturn(world);
when(to.getBlockX()).thenReturn(X);
when(to.getBlockY()).thenReturn(55);
when(to.getBlockZ()).thenReturn(Z);
PlayerMoveEvent e = new PlayerMoveEvent(user.getPlayer(), from, to);
ibe.onPlayerMove(e);
assertFalse(e.isCancelled());
// Confirm no check is done on the island
Mockito.verify(im, Mockito.never());
}
@Test
public void testVerticalVehicleMoveOnly() {
// Move vertically only
Location from = mock(Location.class);
when(from.getWorld()).thenReturn(world);
when(from.getBlockX()).thenReturn(X);
when(from.getBlockY()).thenReturn(50);
when(from.getBlockZ()).thenReturn(Z);
Location to = mock(Location.class);
when(to.getWorld()).thenReturn(world);
when(to.getBlockX()).thenReturn(X);
when(to.getBlockY()).thenReturn(55);
when(to.getBlockZ()).thenReturn(Z);
// Create vehicle and put two players in it.
Vehicle vehicle = mock(Vehicle.class);
Player player2 = mock(Player.class);
List<Entity> passengers = new ArrayList<>();
passengers.add(user.getPlayer());
passengers.add(player2);
when(vehicle.getPassengers()).thenReturn(passengers);
// Move vehicle
ibe.onVehicleMove(new VehicleMoveEvent(vehicle, from, to));
// Confirm no check is done on the island
Mockito.verify(im, Mockito.never());
}
@Test
public void testPlayerMoveIntoBannedIsland() {
// Make player
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(uuid);
// Give player an island
when(im.hasIsland(uuid)).thenReturn(true);
// Place the player just outside island
when(player.getLocation()).thenReturn(outside);
// Add player to the ban list
when(island.isBanned(Mockito.eq(uuid))).thenReturn(true);
// Move player
PlayerMoveEvent e = new PlayerMoveEvent(player, outside, inside);
ibe.onPlayerMove(e);
assertTrue(e.isCancelled());
// Player should see a message
Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString());
// User should NOT be teleported somewhere
Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.eq(player));
}
@Test
public void testPlayerMoveInsideBannedIsland() {
// Make player
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(uuid);
// Give player an island
when(im.hasIsland(uuid)).thenReturn(true);
// Place the player inside island
when(player.getLocation()).thenReturn(inside);
// Add player to the ban list
when(island.isBanned(Mockito.eq(uuid))).thenReturn(true);
// Move player
PlayerMoveEvent e = new PlayerMoveEvent(player, inside, inside2);
ibe.onPlayerMove(e);
assertTrue(e.isCancelled());
// Player should see a message
Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString());
// User should be teleported somewhere
Mockito.verify(im).homeTeleport(Mockito.eq(player));
// Call teleport event
PlayerTeleportEvent ev = new PlayerTeleportEvent(player, inside, outside);
// Pass to event listener
ibe.onPlayerTeleport(ev);
// Should not be cancelled
assertFalse(ev.isCancelled());
}
@Test
public void testVehicleMoveIntoBannedIsland() {
// Make player
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(uuid);
// Give player an island
when(im.hasIsland(uuid)).thenReturn(true);
// Add player to the ban list
when(island.isBanned(Mockito.eq(uuid))).thenReturn(true);
// Add the user to the ban list
when(island.isBanned(Mockito.eq(uuid))).thenReturn(true);
// Create vehicle and put two players in it. One is banned, the other is not
Vehicle vehicle = mock(Vehicle.class);
Player player2 = mock(Player.class);
List<Entity> passengers = new ArrayList<>();
passengers.add(player);
passengers.add(player2);
when(vehicle.getPassengers()).thenReturn(passengers);
// Move vehicle
ibe.onVehicleMove(new VehicleMoveEvent(vehicle, outside, inside));
// Player should see a message and nothing should be sent to Player 2
Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString());
// User should be teleported somewhere
Mockito.verify(im).homeTeleport(Mockito.eq(player));
// Player 2 should not be teleported
Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.eq(player2));
// Call teleport event
PlayerTeleportEvent ev = new PlayerTeleportEvent(player, inside, outside);
// Pass to event listener
ibe.onPlayerTeleport(ev);
// Should not be cancelled
assertFalse(ev.isCancelled());
}
}

View File

@ -128,7 +128,7 @@ public class ObsidianToLavaTest {
// Set as survival
when(who.getGameMode()).thenReturn(GameMode.SURVIVAL);
// Locales - final
// Locales
LocalesManager lm = mock(LocalesManager.class);
when(plugin.getLocalesManager()).thenReturn(lm);
when(lm.get(any(), any())).thenReturn("mock translation");