diff --git a/pom.xml b/pom.xml index 568358b99..97f596d4e 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ jenkins - http://ci.codemc.org/job/BentoBoxWorld/job/BentoBox + https://ci.codemc.org/job/BentoBoxWorld/job/BentoBox @@ -87,8 +87,9 @@ - - + + ci @@ -101,10 +102,14 @@ - - - - + + + + master @@ -115,7 +120,7 @@ ${build.version} - + @@ -130,7 +135,7 @@ org.sonarsource.scanner.maven sonar-maven-plugin - + 3.6.0.1398 @@ -161,78 +166,81 @@ placeholderapi-repo - http://repo.extendedclip.com/content/repositories/placeholderapi/ + https://repo.extendedclip.com/content/repositories/placeholderapi/ dynmap-repo - http://repo.mikeprimm.com/ + https://repo.mikeprimm.com/ worldedit-repo - http://maven.sk89q.com/repo/ + https://maven.sk89q.com/repo/ + + + papermc + https://papermc.io/repo/repository/maven-public/ - - - - org.spigotmc - spigot-api - ${spigot.version} - provided - - - - org.bstats - bstats-bukkit - ${bstats.version} - - - - org.mockito - mockito-core - 3.1.0 - test - - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito2 - ${powermock.version} - test - - - - org.mongodb - mongodb-driver - ${mongodb.version} - - - - - com.github.MilkBowl - VaultAPI - ${vault.version} - provided - - - - me.clip - placeholderapi - ${placeholderapi.version} - provided - + + - - com.github.Prouser123-forks + org.spigotmc + spigot-api + ${spigot.version} + provided + + + + org.bstats + bstats-bukkit + ${bstats.version} + + + + org.mockito + mockito-core + 3.1.0 + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito2 + ${powermock.version} + test + + + + org.mongodb + mongodb-driver + ${mongodb.version} + + + + + com.github.MilkBowl + VaultAPI + ${vault.version} + provided + + + + me.clip + placeholderapi + ${placeholderapi.version} + provided + + + + com.github.BentoBoxWorld MVdWPlaceholderAPI ${mvdwplaceholderapi.version} @@ -255,26 +263,35 @@ GitHubWebAPI4Java ${githubapi.version} - - + + org.eclipse.jdt org.eclipse.jdt.annotation 2.2.200 + + + io.papermc + paperlib + 1.0.2 + compile + - + - + ${project.name}-${revision}${build.number} - + clean package @@ -350,6 +367,10 @@ io.github.TheBusyBiscuit.GitHubWebAPI4Java world.bentobox.bentobox.api.github + + io.papermc.lib + world.bentobox.bentobox.paperlib + @@ -384,8 +405,8 @@ true - + **/*Names* diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java index fd82408bc..c88b30038 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java @@ -1,11 +1,11 @@ package world.bentobox.bentobox.listeners.flags.protection; import org.bukkit.Material; +import org.bukkit.entity.AbstractVillager; import org.bukkit.entity.Animals; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Boat; import org.bukkit.entity.Vehicle; -import org.bukkit.entity.Villager; import org.bukkit.entity.minecart.RideableMinecart; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -47,7 +47,7 @@ public class EntityInteractListener extends FlagListener { } } // Villager trading - else if (e.getRightClicked() instanceof Villager) { + else if (e.getRightClicked() instanceof AbstractVillager) { // Check naming and check trading checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.TRADING); if (e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.NAME_TAG)) { diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListener.java index 61ac70d16..17bdd4917 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListener.java @@ -1,19 +1,24 @@ package world.bentobox.bentobox.listeners.flags.protection; import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; import org.bukkit.Material; +import org.bukkit.entity.AbstractVillager; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Firework; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Parrot; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; -import org.bukkit.entity.Villager; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.EntityShootBowEvent; import org.bukkit.event.entity.LingeringPotionSplashEvent; import org.bukkit.event.entity.PotionSplashEvent; import org.bukkit.event.player.PlayerFishEvent; @@ -35,7 +40,8 @@ import world.bentobox.bentobox.versions.ServerCompatibility; */ public class HurtingListener extends FlagListener { - private HashMap thrownPotions = new HashMap<>(); + private Map thrownPotions = new HashMap<>(); + private Map firedFireworks = new WeakHashMap<>(); /** * Handles mob and monster protection @@ -47,7 +53,7 @@ public class HurtingListener extends FlagListener { // Mobs being hurt if (Util.isPassiveEntity(e.getEntity())) { respond(e, e.getDamager(), Flags.HURT_ANIMALS); - } else if (e.getEntity() instanceof Villager) { + } else if (e.getEntity() instanceof AbstractVillager) { respond(e, e.getDamager(), Flags.HURT_VILLAGERS); } else if (Util.isHostileEntity(e.getEntity())) { respond(e, e.getDamager(), Flags.HURT_MONSTERS); @@ -85,7 +91,7 @@ public class HurtingListener extends FlagListener { if ((Util.isPassiveEntity(e.getCaught()) && checkIsland(e, e.getPlayer(), e.getCaught().getLocation(), Flags.HURT_ANIMALS)) || (Util.isHostileEntity(e.getCaught()) && checkIsland(e, e.getPlayer(), e.getCaught().getLocation(), Flags.HURT_MONSTERS)) - || (e.getCaught() instanceof Villager && checkIsland(e, e.getPlayer(), e.getCaught().getLocation(), Flags.HURT_VILLAGERS))) { + || (e.getCaught() instanceof AbstractVillager && checkIsland(e, e.getPlayer(), e.getCaught().getLocation(), Flags.HURT_VILLAGERS))) { e.getHook().remove(); } @@ -139,7 +145,7 @@ public class HurtingListener extends FlagListener { } // Villagers being hurt - if (entity instanceof Villager && !checkIsland(e, attacker, entity.getLocation(), Flags.HURT_VILLAGERS)) { + if (entity instanceof AbstractVillager && !checkIsland(e, attacker, entity.getLocation(), Flags.HURT_VILLAGERS)) { for (PotionEffect effect : e.getPotion().getEffects()) { entity.removePotionEffect(effect.getType()); } @@ -173,23 +179,43 @@ public class HurtingListener extends FlagListener { public void onLingeringPotionDamage(final EntityDamageByEntityEvent e) { if (e.getCause().equals(DamageCause.ENTITY_ATTACK) && thrownPotions.containsKey(e.getDamager().getEntityId())) { Player attacker = thrownPotions.get(e.getDamager().getEntityId()); - // Self damage - if (attacker == null || attacker.equals(e.getEntity())) { - return; - } - Entity entity = e.getEntity(); - // Monsters being hurt - if (Util.isHostileEntity(entity)) { - checkIsland(e, attacker, entity.getLocation(), Flags.HURT_MONSTERS); - } - // Mobs being hurt - if (Util.isPassiveEntity(entity)) { - checkIsland(e, attacker, entity.getLocation(), Flags.HURT_ANIMALS); - } - // Villagers being hurt - if (entity instanceof Villager) { - checkIsland(e, attacker, entity.getLocation(), Flags.HURT_VILLAGERS); - } + processDamage(e, attacker); + } + } + + private void processDamage(EntityDamageByEntityEvent e, Player attacker) { + // Self damage + if (attacker == null || attacker.equals(e.getEntity())) { + return; + } + Entity entity = e.getEntity(); + // Monsters being hurt + if (Util.isHostileEntity(entity)) { + checkIsland(e, attacker, entity.getLocation(), Flags.HURT_MONSTERS); + } + // Mobs being hurt + if (Util.isPassiveEntity(entity)) { + checkIsland(e, attacker, entity.getLocation(), Flags.HURT_ANIMALS); + } + // Villagers being hurt + if (entity instanceof AbstractVillager) { + checkIsland(e, attacker, entity.getLocation(), Flags.HURT_VILLAGERS); + } + + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onFireworkDamage(final EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Firework && firedFireworks.containsKey(e.getDamager())) { + processDamage(e, (Player)firedFireworks.get(e.getDamager())); + } + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onPlayerShootEvent(final EntityShootBowEvent e) { + // Only care about players shooting fireworks + if (e.getEntityType().equals(EntityType.PLAYER) && (e.getProjectile() instanceof Firework)) { + firedFireworks.put(e.getProjectile(), e.getEntity()); } } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/LockAndBanListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/LockAndBanListener.java index 7d26b8412..ef107d9f7 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/LockAndBanListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/LockAndBanListener.java @@ -12,6 +12,7 @@ import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.vehicle.VehicleMoveEvent; import org.bukkit.util.Vector; +import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.flags.FlagListener; import world.bentobox.bentobox.api.user.User; @@ -155,7 +156,7 @@ public class LockAndBanListener extends FlagListener { } else { // There's nothing much we can do. // We'll try to teleport him to the spawn... - player.teleport(player.getWorld().getSpawnLocation()); + PaperLib.teleportAsync(player, player.getWorld().getSpawnLocation()); // Switch him back to the default gamemode. He may die, sorry :( player.setGameMode(getIWM().getDefaultGameMode(player.getWorld())); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java index a62fb0c5e..8ce05bb1a 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java @@ -1,11 +1,14 @@ package world.bentobox.bentobox.listeners.flags.settings; import java.util.HashMap; +import java.util.Map; import java.util.UUID; +import java.util.WeakHashMap; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.entity.Entity; +import org.bukkit.entity.Firework; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; @@ -16,6 +19,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.entity.AreaEffectCloudApplyEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.EntityShootBowEvent; import org.bukkit.event.entity.LingeringPotionSplashEvent; import org.bukkit.event.entity.PotionSplashEvent; import org.bukkit.event.player.PlayerFishEvent; @@ -32,7 +36,8 @@ import world.bentobox.bentobox.lists.Flags; */ public class PVPListener extends FlagListener { - private HashMap thrownPotions = new HashMap<>(); + private Map thrownPotions = new HashMap<>(); + private Map firedFireworks = new WeakHashMap<>(); /** * This method protects players from PVP if it is not allowed and from @@ -79,23 +84,31 @@ public class PVPListener extends FlagListener { } else if (damager instanceof Projectile) { // Find out who fired the arrow Projectile p = (Projectile) damager; - Entity entity =(Entity)p.getShooter(); - if (entity instanceof Player) { - // Allow self damage - if (hurtEntity.equals(entity)) { - return; - } - User user = User.getInstance((Player)p.getShooter()); - if (!checkIsland((Event)e, (Player)entity, damager.getLocation(), flag)) { - damager.setFireTicks(0); - hurtEntity.setFireTicks(0); - user.notify(Flags.PVP_OVERWORLD.getHintReference()); - e.setCancelled(true); - } + Entity shooter =(Entity)p.getShooter(); + if (shooter instanceof Player) { + processDamage(e, damager, (Player)shooter, hurtEntity, flag); } + } else if (damager instanceof Firework && firedFireworks.containsKey(damager)) { + Player shooter = firedFireworks.get(damager); + processDamage(e, damager, shooter, hurtEntity, flag); } } + private void processDamage(Cancellable e, Entity damager, Player shooter, Entity hurtEntity, Flag flag) { + // Allow self damage + if (hurtEntity.equals(shooter)) { + return; + } + User user = User.getInstance(shooter); + if (!checkIsland((Event)e, shooter, damager.getLocation(), flag)) { + damager.setFireTicks(0); + hurtEntity.setFireTicks(0); + user.notify(Flags.PVP_OVERWORLD.getHintReference()); + e.setCancelled(true); + } + + } + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onFishing(PlayerFishEvent e) { if (e.getCaught() instanceof Player && getPlugin().getIWM().inWorld(e.getCaught().getLocation())) { @@ -191,4 +204,11 @@ public class PVPListener extends FlagListener { } } + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled=true) + public void onPlayerShootFireworkEvent(final EntityShootBowEvent e) { + // Only care about players shooting fireworks + if (e.getEntity() instanceof Player && (e.getProjectile() instanceof Firework)) { + firedFireworks.put(e.getProjectile(), (Player)e.getEntity()); + } + } } diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index f3a870990..dd0198d14 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -20,6 +20,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.TreeSpecies; import org.bukkit.World; +import org.bukkit.attribute.Attribute; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; @@ -36,6 +37,7 @@ import org.eclipse.jdt.annotation.Nullable; import com.google.common.collect.ImmutableMap; +import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.island.IslandEvent; @@ -664,27 +666,26 @@ public class IslandsManager { .build(); return; } - if (!home.getChunk().isLoaded()) { - home.getChunk().load(); - } - player.teleport(home); // Add home if (plugin.getPlayers().getHomeLocations(world, player.getUniqueId()).isEmpty()) { plugin.getPlayers().setHomeLocation(player.getUniqueId(), home); } - if (number == 1) { - user.sendMessage("commands.island.go.teleport"); - } else { + user.sendMessage("commands.island.go.teleport"); + PaperLib.teleportAsync(player, home).thenAccept(b -> teleported(world, user, number, newIsland)); + } + + private void teleported(World world, User user, int number, boolean newIsland) { + if (number > 1) { user.sendMessage("commands.island.go.teleported", TextVariables.NUMBER, String.valueOf(number)); } // If this is a new island, then run commands and do resets if (newIsland) { // Execute commands plugin.getIWM().getOnJoinCommands(world).forEach(command -> { - command = command.replace("[player]", player.getName()); + command = command.replace("[player]", user.getName()); if (command.startsWith("[SUDO]")) { // Execute the command by the player - player.performCommand(command.substring(6)); + user.performCommand(command.substring(6)); } else { // Otherwise execute as the server console plugin.getServer().dispatchCommand(Bukkit.getConsoleSender(), command); @@ -704,7 +705,7 @@ public class IslandsManager { // Reset the health if (plugin.getIWM().isOnJoinResetHealth(world)) { - user.getPlayer().setHealth(20.0D); + user.getPlayer().setHealth(user.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getDefaultValue()); } // Reset the hunger @@ -789,7 +790,7 @@ public class IslandsManager { this.spawn.put(spawn.getWorld(), spawn); spawn.setSpawn(true); } - + /** * Clears the spawn island for this world * @param world - world @@ -986,7 +987,7 @@ public class IslandsManager { // Move player to spawn if (spawn.containsKey(w)) { // go to island spawn - p.teleport(spawn.get(w).getSpawnPoint(w.getEnvironment())); + PaperLib.teleportAsync(p, spawn.get(w).getSpawnPoint(w.getEnvironment())); } } }); diff --git a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java index 9ea677d39..457619c51 100644 --- a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java +++ b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java @@ -5,6 +5,8 @@ import java.util.Random; import org.bukkit.Bukkit; import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.generator.ChunkGenerator; @@ -12,6 +14,7 @@ import org.bukkit.generator.ChunkGenerator.ChunkData; import org.bukkit.inventory.InventoryHolder; import org.bukkit.scheduler.BukkitTask; +import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.events.island.IslandEvent; @@ -25,13 +28,11 @@ import world.bentobox.bentobox.database.objects.IslandDeletion; */ public class DeleteIslandChunks { - /** - * This is how many chunks per world will be done in one tick. - */ private int chunkX; private int chunkZ; private BukkitTask task; private IslandDeletion di; + private boolean inDelete; public DeleteIslandChunks(BentoBox plugin, IslandDeletion di) { // Fire event @@ -42,16 +43,19 @@ public class DeleteIslandChunks { this.di = di; // Run through all chunks of the islands and regenerate them. task = Bukkit.getScheduler().runTaskTimer(plugin, () -> { + if (inDelete) return; + inDelete = true; for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) { plugin.getIWM().getAddon(di.getWorld()).ifPresent(gm -> { - Chunk chunk = di.getWorld().getChunkAt(chunkX, chunkZ); - regenerateChunk(gm, chunk); - + // Overworld + processChunk(gm, di.getWorld(), chunkX, chunkZ); + // Nether if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) { - regenerateChunk(gm, plugin.getIWM().getNetherWorld(di.getWorld()).getChunkAt(chunkX, chunkZ)); + processChunk(gm, plugin.getIWM().getNetherWorld(di.getWorld()), chunkX, chunkZ); } + // End if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) { - regenerateChunk(gm, plugin.getIWM().getEndWorld(di.getWorld()).getChunkAt(chunkX, chunkZ)); + processChunk(gm, plugin.getIWM().getEndWorld(di.getWorld()), chunkX, chunkZ); } chunkZ++; if (chunkZ > di.getMaxZChunk()) { @@ -66,9 +70,16 @@ public class DeleteIslandChunks { } }); } + inDelete = false; }, 0L, 1L); } + private void processChunk(GameModeAddon gm, World world, int x, int z) { + if (PaperLib.isChunkGenerated(world, x, z)) { + PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk -> regenerateChunk(gm, chunk)); + } + } + private void regenerateChunk(GameModeAddon gm, Chunk chunk) { boolean isLoaded = chunk.isLoaded(); // Clear all inventories @@ -88,6 +99,9 @@ public class DeleteIslandChunks { if (di.inBounds(baseX + x, baseZ + z)) { chunk.getBlock(x, 0, z).setBiome(grid.getBiome(x, z)); for (int y = 0; y < chunk.getWorld().getMaxHeight(); y++) { + // Note: setting block to air before setting it to something else stops a bug in the server + // where it reports a " + chunk.getBlock(x, y, z).setType(Material.AIR, false); chunk.getBlock(x, y, z).setBlockData(cd.getBlockData(x, y, z), false); } } diff --git a/src/main/java/world/bentobox/bentobox/util/Util.java b/src/main/java/world/bentobox/bentobox/util/Util.java index eb469580f..797c23746 100644 --- a/src/main/java/world/bentobox/bentobox/util/Util.java +++ b/src/main/java/world/bentobox/bentobox/util/Util.java @@ -6,14 +6,19 @@ import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.List; +import java.util.concurrent.CompletableFuture; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.stream.Collectors; +import javax.annotation.Nonnull; + import org.bukkit.Bukkit; +import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.World.Environment; +import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Animals; import org.bukkit.entity.Bat; @@ -28,10 +33,13 @@ import org.bukkit.entity.Shulker; import org.bukkit.entity.Slime; import org.bukkit.entity.Snowman; import org.bukkit.entity.WaterMob; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import io.papermc.lib.PaperLib; +import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.user.User; @@ -351,4 +359,164 @@ public class Util { return entity instanceof Animals || entity instanceof IronGolem || entity instanceof Snowman || entity instanceof WaterMob && !(entity instanceof PufferFish) || entity instanceof Bat; } + + /* + * PaperLib methods for addons to call + */ + + /** + * Teleports an Entity to the target location, loading the chunk asynchronously first if needed. + * @param entity The Entity to teleport + * @param location The Location to Teleport to + * @return Future that completes with the result of the teleport + */ + @Nonnull + public static CompletableFuture teleportAsync(@Nonnull Entity entity, @Nonnull Location location) { + return PaperLib.teleportAsync(entity, location); + } + + /** + * Teleports an Entity to the target location, loading the chunk asynchronously first if needed. + * @param entity The Entity to teleport + * @param location The Location to Teleport to + * @param cause The cause for the teleportation + * @return Future that completes with the result of the teleport + */ + @Nonnull + public static CompletableFuture teleportAsync(@Nonnull Entity entity, @Nonnull Location location, TeleportCause cause) { + return PaperLib.teleportAsync(entity, location, cause); + } + + /** + * Gets the chunk at the target location, loading it asynchronously if needed. + * @param loc Location to get chunk for + * @return Future that completes with the chunk + */ + @Nonnull + public static CompletableFuture getChunkAtAsync(@Nonnull Location loc) { + return getChunkAtAsync(loc.getWorld(), loc.getBlockX() >> 4, loc.getBlockZ() >> 4, true); + } + + /** + * Gets the chunk at the target location, loading it asynchronously if needed. + * @param loc Location to get chunk for + * @param gen Should the chunk generate or not. Only respected on some MC versions, 1.13 for CB, 1.12 for Paper + * @return Future that completes with the chunk, or null if the chunk did not exists and generation was not requested. + */ + @Nonnull + public static CompletableFuture getChunkAtAsync(@Nonnull Location loc, boolean gen) { + return getChunkAtAsync(loc.getWorld(), loc.getBlockX() >> 4, loc.getBlockZ() >> 4, gen); + } + + /** + * Gets the chunk at the target location, loading it asynchronously if needed. + * @param world World to load chunk for + * @param x X coordinate of the chunk to load + * @param z Z coordinate of the chunk to load + * @return Future that completes with the chunk + */ + @Nonnull + public static CompletableFuture getChunkAtAsync(@Nonnull World world, int x, int z) { + return getChunkAtAsync(world, x, z, true); + } + + /** + * Gets the chunk at the target location, loading it asynchronously if needed. + * @param world World to load chunk for + * @param x X coordinate of the chunk to load + * @param z Z coordinate of the chunk to load + * @param gen Should the chunk generate or not. Only respected on some MC versions, 1.13 for CB, 1.12 for Paper + * @return Future that completes with the chunk, or null if the chunk did not exists and generation was not requested. + */ + @Nonnull + public static CompletableFuture getChunkAtAsync(@Nonnull World world, int x, int z, boolean gen) { + return PaperLib.getChunkAtAsync(world, x, z, gen); + } + + /** + * Checks if the chunk has been generated or not. Only works on Paper 1.12+ or any 1.13.1+ version + * @param loc Location to check if the chunk is generated + * @return If the chunk is generated or not + */ + public static boolean isChunkGenerated(@Nonnull Location loc) { + return isChunkGenerated(loc.getWorld(), loc.getBlockX() >> 4, loc.getBlockZ() >> 4); + } + + /** + * Checks if the chunk has been generated or not. Only works on Paper 1.12+ or any 1.13.1+ version + * @param world World to check for + * @param x X coordinate of the chunk to check + * @param z Z coordinate of the chunk to checl + * @return If the chunk is generated or not + */ + public static boolean isChunkGenerated(@Nonnull World world, int x, int z) { + return PaperLib.isChunkGenerated(world, x, z); + } + + /** + * Get's a BlockState, optionally not using a snapshot + * @param block The block to get a State of + * @param useSnapshot Whether or not to use a snapshot when supported + * @return The BlockState + */ + @Nonnull + public static BlockStateSnapshotResult getBlockState(@Nonnull Block block, boolean useSnapshot) { + return PaperLib.getBlockState(block, useSnapshot); + } + + /** + * Detects if the current MC version is at least the following version. + * + * Assumes 0 patch version. + * + * @param minor Min Minor Version + * @return Meets the version requested + */ + public static boolean isVersion(int minor) { + return PaperLib.isVersion(minor); + } + + /** + * Detects if the current MC version is at least the following version. + * @param minor Min Minor Version + * @param patch Min Patch Version + * @return Meets the version requested + */ + public static boolean isVersion(int minor, int patch) { + return PaperLib.isVersion(minor, patch); + } + + /** + * Gets the current Minecraft Minor version. IE: 1.13.1 returns 13 + * @return The Minor Version + */ + public static int getMinecraftVersion() { + return PaperLib.getMinecraftVersion(); + } + + /** + * Gets the current Minecraft Patch version. IE: 1.13.1 returns 1 + * @return The Patch Version + */ + public static int getMinecraftPatchVersion() { + return PaperLib.getMinecraftPatchVersion(); + } + + /** + * Check if the server has access to the Spigot API + * @return True for Spigot and Paper environments + */ + public static boolean isSpigot() { + return PaperLib.isSpigot(); + } + + /** + * Check if the server has access to the Paper API + * @return True for Paper environments + */ + public static boolean isPaper() { + return PaperLib.isPaper(); + } + + } diff --git a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java index b5b31001c..895280bd0 100644 --- a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java +++ b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java @@ -17,6 +17,7 @@ import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.Nullable; +import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; @@ -226,16 +227,13 @@ public class SafeSpotTeleport { */ private void teleportEntity(final Location loc) { task.cancel(); + if (!portal && entity instanceof Player && homeNumber > 0) { + // Set home if so marked + plugin.getPlayers().setHomeLocation(User.getInstance(entity), loc, homeNumber); + } + Vector velocity = entity.getVelocity(); // Return to main thread and teleport the player - Bukkit.getScheduler().runTask(plugin, () -> { - if (!portal && entity instanceof Player && homeNumber > 0) { - // Set home if so marked - plugin.getPlayers().setHomeLocation(User.getInstance(entity), loc, homeNumber); - } - Vector velocity = entity.getVelocity(); - entity.teleport(loc); - entity.setVelocity(velocity); - }); + Bukkit.getScheduler().runTask(plugin, () -> PaperLib.teleportAsync(entity, loc).thenAccept(b -> entity.setVelocity(velocity))); } /** diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java index 8fcc3337e..8bcdee8a1 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java @@ -1,10 +1,15 @@ package world.bentobox.bentobox.listeners.flags.protection; -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.util.Collections; import java.util.Optional; @@ -21,6 +26,7 @@ import org.bukkit.entity.Horse; import org.bukkit.entity.Player; import org.bukkit.entity.Sheep; import org.bukkit.entity.Villager; +import org.bukkit.entity.WanderingTrader; import org.bukkit.entity.minecart.RideableMinecart; import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; @@ -329,6 +335,63 @@ public class EntityInteractListenerTest { verify(notifier).notify(any(), eq("protection.protected")); assertTrue(e.isCancelled()); } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractEntityEvent)}. + */ + @Test + public void testOnPlayerInteractEntityWanderingTraderNoInteraction() { + clickedEntity = mock(WanderingTrader.class); + when(clickedEntity.getLocation()).thenReturn(location); + PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); + eil.onPlayerInteractEntity(e); + verify(notifier, times(2)).notify(any(), eq("protection.protected")); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent)}. + */ + @Test + public void testOnPlayerInteractAtEntityWanderingTraderAllowed() { + when(island.isAllowed(any(), any())).thenReturn(true); + clickedEntity = mock(WanderingTrader.class); + when(clickedEntity.getLocation()).thenReturn(location); + PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); + eil.onPlayerInteractEntity(e); + verify(notifier, never()).notify(any(), eq("protection.protected")); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent)}. + */ + @Test + public void testOnPlayerInteractEntityNamingWanderingTraderAllowedNoTrading() { + when(island.isAllowed(any(), eq(Flags.TRADING))).thenReturn(false); + when(island.isAllowed(any(), eq(Flags.NAME_TAG))).thenReturn(true); + clickedEntity = mock(WanderingTrader.class); + when(clickedEntity.getLocation()).thenReturn(location); + PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); + eil.onPlayerInteractEntity(e); + verify(notifier).notify(any(), eq("protection.protected")); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent)}. + */ + @Test + public void testOnPlayerInteractEntityNamingWanderingTraderAllowedTradingNoNaming() { + when(island.isAllowed(any(), eq(Flags.TRADING))).thenReturn(true); + when(island.isAllowed(any(), eq(Flags.NAME_TAG))).thenReturn(false); + clickedEntity = mock(WanderingTrader.class); + when(clickedEntity.getLocation()).thenReturn(location); + PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); + eil.onPlayerInteractEntity(e); + verify(notifier).notify(any(), eq("protection.protected")); + assertTrue(e.isCancelled()); + } /** * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractEntityEvent)}. diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListenerTest.java index 405ed0912..2d3dbc965 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListenerTest.java @@ -1,6 +1,3 @@ -/** - * - */ package world.bentobox.bentobox.listeners.flags.protection; import static org.junit.Assert.assertFalse; @@ -30,6 +27,7 @@ import org.bukkit.entity.Monster; import org.bukkit.entity.Player; import org.bukkit.entity.Slime; import org.bukkit.entity.Villager; +import org.bukkit.entity.WanderingTrader; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.player.PlayerFishEvent; import org.bukkit.event.player.PlayerFishEvent.State; @@ -333,6 +331,22 @@ public class HurtingListenerTest { // Verify verify(notifier).notify(eq(user), eq("protection.protected")); } + + /** + * Test method for {@link HurtingListener#onFishing(org.bukkit.event.player.PlayerFishEvent)}. + */ + @Test + public void testOnFishingDisallowWanderingTraderCatching() { + WanderingTrader entity = mock(WanderingTrader.class); + when(entity.getLocation()).thenReturn(location); + State state = State.CAUGHT_ENTITY; + PlayerFishEvent e = new PlayerFishEvent(player, entity, hookEntity, state); + HurtingListener hl = new HurtingListener(); + hl.onFishing(e); + // Verify + verify(notifier).notify(eq(user), eq("protection.protected")); + } + /** * Test method for {@link HurtingListener#onFishing(org.bukkit.event.player.PlayerFishEvent)}. @@ -350,6 +364,24 @@ public class HurtingListenerTest { // Verify verify(notifier, never()).notify(eq(user), eq("protection.protected")); } + + /** + * Test method for {@link HurtingListener#onFishing(org.bukkit.event.player.PlayerFishEvent)}. + */ + @Test + public void testOnFishingAllowWanderingTraderCatching() { + WanderingTrader entity = mock(WanderingTrader.class); + when(entity.getLocation()).thenReturn(location); + State state = State.CAUGHT_ENTITY; + PlayerFishEvent e = new PlayerFishEvent(player, entity, hookEntity, state); + HurtingListener hl = new HurtingListener(); + // Allow + when(island.isAllowed(any(), any())).thenReturn(true); + hl.onFishing(e); + // Verify + verify(notifier, never()).notify(eq(user), eq("protection.protected")); + } + /** * Test method for {@link HurtingListener#onPlayerFeedParrots(org.bukkit.event.player.PlayerInteractEntityEvent)}. */ diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListenerTest.java index fe583ff18..2c1f17a86 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListenerTest.java @@ -29,6 +29,7 @@ import org.bukkit.block.ShulkerBox; import org.bukkit.entity.Horse; import org.bukkit.entity.Player; import org.bukkit.entity.Villager; +import org.bukkit.entity.WanderingTrader; import org.bukkit.entity.minecart.StorageMinecart; import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryAction; @@ -76,7 +77,7 @@ public class InventoryListenerTest { private final static List> HOLDERS = Arrays.asList(Horse.class, Chest.class,ShulkerBox.class, StorageMinecart.class, Dispenser.class, Dropper.class, Hopper.class, Furnace.class, BrewingStand.class, - Villager.class); + Villager.class, WanderingTrader.class); private Location location; private BentoBox plugin; diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/settings/PVPListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/settings/PVPListenerTest.java index 2257d0314..d671d2e53 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/settings/PVPListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/settings/PVPListenerTest.java @@ -3,13 +3,13 @@ package world.bentobox.bentobox.listeners.flags.settings; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.times; import java.util.ArrayList; import java.util.Collections; @@ -22,10 +22,14 @@ import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.World; import org.bukkit.entity.AreaEffectCloud; +import org.bukkit.entity.Arrow; import org.bukkit.entity.Creeper; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Firework; import org.bukkit.entity.FishHook; import org.bukkit.entity.LingeringPotion; import org.bukkit.entity.LivingEntity; @@ -37,7 +41,9 @@ import org.bukkit.entity.Zombie; import org.bukkit.event.entity.AreaEffectCloudApplyEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; +import org.bukkit.event.entity.EntityShootBowEvent; import org.bukkit.event.entity.LingeringPotionSplashEvent; import org.bukkit.event.entity.PotionSplashEvent; import org.bukkit.event.player.PlayerFishEvent; @@ -49,6 +55,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; @@ -87,15 +94,25 @@ import world.bentobox.bentobox.util.Util; @PrepareForTest({BentoBox.class, Util.class, Bukkit.class }) public class PVPListenerTest { + @Mock private IslandWorldManager iwm; + @Mock private IslandsManager im; + @Mock private Island island; + @Mock private Player player; + @Mock private Player player2; + @Mock private Location loc; + @Mock private Zombie zombie; + @Mock private Creeper creeper; + @Mock private World world; + @Mock private Notifier notifier; /** @@ -109,7 +126,6 @@ public class PVPListenerTest { // Make sure you set the plung for the User class otherwise it'll use an old object User.setPlugin(plugin); // Island World Manager - iwm = mock(IslandWorldManager.class); when(iwm.inWorld(any(World.class))).thenReturn(true); when(iwm.inWorld(any(Location.class))).thenReturn(true); when(iwm.getPermissionPrefix(Mockito.any())).thenReturn("bskyblock."); @@ -120,23 +136,23 @@ public class PVPListenerTest { Panel panel = mock(Panel.class); when(panel.getInventory()).thenReturn(mock(Inventory.class)); - // Sometimes use Mockito.withSettings().verboseLogging() - player = mock(Player.class); - UUID uuid = UUID.randomUUID(); - when(player.getUniqueId()).thenReturn(uuid); - world = mock(World.class); + // World when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); - when(player.getWorld()).thenReturn(world); - loc = mock(Location.class); + // Location when(loc.getWorld()).thenReturn(world); + // Sometimes use Mockito.withSettings().verboseLogging() + // Player + UUID uuid = UUID.randomUUID(); + when(player.getUniqueId()).thenReturn(uuid); when(player.getLocation()).thenReturn(loc); + when(player.getWorld()).thenReturn(world); User.getInstance(player); // Sometimes use Mockito.withSettings().verboseLogging() - player2 = mock(Player.class); + // Player 2 UUID uuid2 = UUID.randomUUID(); when(player2.getUniqueId()).thenReturn(uuid2); @@ -144,9 +160,11 @@ public class PVPListenerTest { when(player2.getLocation()).thenReturn(loc); User.getInstance(player2); + // Util PowerMockito.mockStatic(Util.class); when(Util.getWorld(any())).thenReturn(mock(World.class)); + // Flags Manager FlagsManager fm = mock(FlagsManager.class); Flag flag = mock(Flag.class); when(flag.isSetForWorld(any())).thenReturn(false); @@ -156,7 +174,7 @@ public class PVPListenerTest { when(fm.getFlag(Mockito.anyString())).thenReturn(Optional.of(flag)); when(plugin.getFlagsManager()).thenReturn(fm); - im = mock(IslandsManager.class); + // Island Manager // Default is that player in on their island when(im.userIsOnIsland(any(), any())).thenReturn(true); island = mock(Island.class); @@ -182,12 +200,12 @@ public class PVPListenerTest { when(placeholdersManager.replacePlaceholders(any(), any())).thenAnswer(answer); // Create some entities - zombie = mock(Zombie.class); when(zombie.getWorld()).thenReturn(world); when(zombie.getUniqueId()).thenReturn(UUID.randomUUID()); - creeper = mock(Creeper.class); + when(zombie.getType()).thenReturn(EntityType.ZOMBIE); when(creeper.getWorld()).thenReturn(world); when(creeper.getUniqueId()).thenReturn(UUID.randomUUID()); + when(creeper.getType()).thenReturn(EntityType.CREEPER); // Scheduler BukkitScheduler sch = mock(BukkitScheduler.class); @@ -204,7 +222,6 @@ public class PVPListenerTest { when(iwm.getAddon(any())).thenReturn(opGma); // Notifier - notifier = mock(Notifier.class); when(plugin.getNotifier()).thenReturn(notifier); // Addon @@ -1000,4 +1017,102 @@ public class PVPListenerTest { assertFalse(ae.getAffectedEntities().contains(player2)); verify(notifier).notify(any(), eq(Flags.INVINCIBLE_VISITORS.getHintReference())); } + + /** + * Test method for {@link PVPListener#onPlayerShootFireworkEvent(org.bukkit.event.entity.EntityShootBowEvent)}. + */ + @Test + public void testOnPlayerShootFireworkEventNotPlayer() { + PVPListener listener = new PVPListener(); + ItemStack bow = new ItemStack(Material.CROSSBOW); + Firework firework = mock(Firework.class); + when(firework.getEntityId()).thenReturn(123); + EntityShootBowEvent e = new EntityShootBowEvent(creeper, bow, firework, 0); + listener.onPlayerShootFireworkEvent(e); + + // Now damage + EntityDamageByEntityEvent en = new EntityDamageByEntityEvent(firework, player, DamageCause.ENTITY_ATTACK, 0); + listener.onEntityDamage(en); + assertFalse(en.isCancelled()); + } + + /** + * Test method for {@link PVPListener#onPlayerShootFireworkEvent(org.bukkit.event.entity.EntityShootBowEvent)}. + */ + @Test + public void testOnPlayerShootFireworkEventNotFirework() { + PVPListener listener = new PVPListener(); + ItemStack bow = new ItemStack(Material.CROSSBOW); + Arrow arrow = mock(Arrow.class); + EntityShootBowEvent e = new EntityShootBowEvent(creeper, bow, arrow, 0); + listener.onPlayerShootFireworkEvent(e); + // Now damage + EntityDamageByEntityEvent en = new EntityDamageByEntityEvent(arrow, player, DamageCause.ENTITY_ATTACK, 0); + listener.onEntityDamage(en); + assertFalse(en.isCancelled()); + } + + /** + * Test method for {@link PVPListener#onPlayerShootFireworkEvent(org.bukkit.event.entity.EntityShootBowEvent)}. + */ + @Test + public void testOnPlayerShootFireworkEventNoPVPSelfDamage() { + // Disallow PVP + when(island.isAllowed(any())).thenReturn(false); + PVPListener listener = new PVPListener(); + ItemStack bow = new ItemStack(Material.CROSSBOW); + Firework firework = mock(Firework.class); + when(firework.getEntityId()).thenReturn(123); + when(firework.getLocation()).thenReturn(loc); + EntityShootBowEvent e = new EntityShootBowEvent(player, bow, firework, 0); + listener.onPlayerShootFireworkEvent(e); + + // Now damage + EntityDamageByEntityEvent en = new EntityDamageByEntityEvent(firework, player, DamageCause.ENTITY_EXPLOSION, 0); + listener.onEntityDamage(en); + assertFalse(en.isCancelled()); + } + + /** + * Test method for {@link PVPListener#onPlayerShootFireworkEvent(org.bukkit.event.entity.EntityShootBowEvent)}. + */ + @Test + public void testOnPlayerShootFireworkEventNoPVP() { + // Disallow PVP + when(island.isAllowed(any())).thenReturn(false); + PVPListener listener = new PVPListener(); + ItemStack bow = new ItemStack(Material.CROSSBOW); + Firework firework = mock(Firework.class); + when(firework.getEntityId()).thenReturn(123); + when(firework.getLocation()).thenReturn(loc); + EntityShootBowEvent e = new EntityShootBowEvent(player, bow, firework, 0); + listener.onPlayerShootFireworkEvent(e); + + // Now damage + EntityDamageByEntityEvent en = new EntityDamageByEntityEvent(firework, player2, DamageCause.ENTITY_EXPLOSION, 0); + listener.onEntityDamage(en); + assertTrue(en.isCancelled()); + } + + /** + * Test method for {@link PVPListener#onPlayerShootFireworkEvent(org.bukkit.event.entity.EntityShootBowEvent)}. + */ + @Test + public void testOnPlayerShootFireworkEventPVPAllowed() { + // Allow PVP + when(island.isAllowed(any())).thenReturn(true); + PVPListener listener = new PVPListener(); + ItemStack bow = new ItemStack(Material.CROSSBOW); + Firework firework = mock(Firework.class); + when(firework.getEntityId()).thenReturn(123); + when(firework.getLocation()).thenReturn(loc); + EntityShootBowEvent e = new EntityShootBowEvent(player, bow, firework, 0); + listener.onPlayerShootFireworkEvent(e); + + // Now damage + EntityDamageByEntityEvent en = new EntityDamageByEntityEvent(firework, player2, DamageCause.ENTITY_EXPLOSION, 0); + listener.onEntityDamage(en); + assertFalse(en.isCancelled()); + } + } diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index a4682a7ab..3761b2744 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -66,6 +66,9 @@ import org.powermock.reflect.Whitebox; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; +import io.papermc.lib.PaperLib; +import io.papermc.lib.environments.CraftBukkitEnvironment; +import io.papermc.lib.environments.Environment; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.api.configuration.WorldSettings; @@ -131,6 +134,8 @@ public class IslandsManagerTest { private Material sign; private Material wallSign; + private Environment env; + /** * @throws java.lang.Exception @@ -188,6 +193,8 @@ public class IslandsManagerTest { BukkitScheduler sch = mock(BukkitScheduler.class); PowerMockito.mockStatic(Bukkit.class); when(Bukkit.getScheduler()).thenReturn(sch); + // version + when(Bukkit.getVersion()).thenReturn("Paper version git-Paper-225 (MC: 1.14.4) (Implementing API version 1.14.4-R0.1-SNAPSHOT)"); // Standard location manager = new IslandsManager(plugin); @@ -295,6 +302,10 @@ public class IslandsManagerTest { if (wallSign == null) { wallSign = Material.getMaterial("OAK_WALL_SIGN"); } + + // PaperLib + env = new CraftBukkitEnvironment(); + PaperLib.setCustomEnvironment(env); } @After @@ -716,7 +727,7 @@ public class IslandsManagerTest { when(pm.getHomeLocation(any(), any(User.class), eq(0))).thenReturn(null); when(pm.getHomeLocation(any(), any(User.class), eq(1))).thenReturn(location); im.homeTeleport(world, player, 0); - verify(player).teleport(location); + verify(player).teleport(eq(location), any()); }