From c9b0c74d085391ed7e6bd49314ddf66adace15e1 Mon Sep 17 00:00:00 2001 From: tastybento Date: Wed, 13 Nov 2019 15:10:33 -0800 Subject: [PATCH 1/3] Paper lib (#1026) * Remove unused imports * Implements PaperLib within bentobox * Added PaperLib methods to Util for addons to call * Uses PaperLib for island deletion * Point to https://github.com/BentoBoxWorld/MVdWPlaceholderAPI in POM --- pom.xml | 181 ++++++++++-------- .../bentobox/listeners/JoinLeaveListener.java | 136 +++++++------ .../flags/protection/LockAndBanListener.java | 3 +- .../bentobox/managers/IslandsManager.java | 25 +-- .../bentobox/panels/IslandCreationPanel.java | 1 - .../bentobox/util/DeleteIslandChunks.java | 30 ++- .../world/bentobox/bentobox/util/Util.java | 168 ++++++++++++++++ .../util/teleport/SafeSpotTeleport.java | 16 +- .../bentobox/managers/IslandsManagerTest.java | 13 +- 9 files changed, 392 insertions(+), 181 deletions(-) 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/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index d871dcc82..f08ddc175 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java @@ -3,7 +3,6 @@ package world.bentobox.bentobox.listeners; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.UUID; import org.bukkit.Bukkit; @@ -15,7 +14,6 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.scheduler.BukkitRunnable; import org.eclipse.jdt.annotation.NonNull; import world.bentobox.bentobox.BentoBox; @@ -57,32 +55,32 @@ public class JoinLeaveListener implements Listener { players.addPlayer(playerUUID); plugin.getIWM().getOverWorlds().stream() - .filter(w -> plugin.getIWM().isCreateIslandOnFirstLoginEnabled(w)) - .forEach(w -> { - // Even if that'd be extremely unlikely, it's better to check if the player doesn't have an already. - if (!(plugin.getIslands().hasIsland(w, user) || plugin.getIslands().inTeam(w, user.getUniqueId()))) { - int delay = plugin.getIWM().getCreateIslandOnFirstLoginDelay(w); - user.sendMessage("commands.island.create.on-first-login", - TextVariables.NUMBER, String.valueOf(delay)); + .filter(w -> plugin.getIWM().isCreateIslandOnFirstLoginEnabled(w)) + .forEach(w -> { + // Even if that'd be extremely unlikely, it's better to check if the player doesn't have an already. + if (!(plugin.getIslands().hasIsland(w, user) || plugin.getIslands().inTeam(w, user.getUniqueId()))) { + int delay = plugin.getIWM().getCreateIslandOnFirstLoginDelay(w); + user.sendMessage("commands.island.create.on-first-login", + TextVariables.NUMBER, String.valueOf(delay)); - Runnable createIsland = () -> { - // should only execute if: - // - abort on logout is false - // - abort on logout is true && user is online - if (!plugin.getIWM().isCreateIslandOnFirstLoginAbortOnLogout(w) || user.isOnline()){ - plugin.getIWM().getAddon(w).ifPresent(addon -> addon.getPlayerCommand() - .map(command -> command.getSubCommand("create").orElse(null)) - .ifPresent(command -> command.execute(user, "create", Collections.singletonList(BlueprintsManager.DEFAULT_BUNDLE_NAME)))); - } - }; - - if (delay <= 0) { - createIsland.run(); - } else { - plugin.getServer().getScheduler().runTaskLater(plugin, createIsland, delay * 20L); - } + Runnable createIsland = () -> { + // should only execute if: + // - abort on logout is false + // - abort on logout is true && user is online + if (!plugin.getIWM().isCreateIslandOnFirstLoginAbortOnLogout(w) || user.isOnline()){ + plugin.getIWM().getAddon(w).ifPresent(addon -> addon.getPlayerCommand() + .map(command -> command.getSubCommand("create").orElse(null)) + .ifPresent(command -> command.execute(user, "create", Collections.singletonList(BlueprintsManager.DEFAULT_BUNDLE_NAME)))); } - }); + }; + + if (delay <= 0) { + createIsland.run(); + } else { + plugin.getServer().getScheduler().runTaskLater(plugin, createIsland, delay * 20L); + } + } + }); } // Make sure the player is loaded into the cache (doesn't impact performance) @@ -90,8 +88,8 @@ public class JoinLeaveListener implements Listener { // Reset island resets if required plugin.getIWM().getOverWorlds().stream() - .filter(w -> event.getPlayer().getLastPlayed() < plugin.getIWM().getResetEpoch(w)) - .forEach(w -> players.setResets(w, playerUUID, 0)); + .filter(w -> event.getPlayer().getLastPlayed() < plugin.getIWM().getResetEpoch(w)) + .forEach(w -> players.setResets(w, playerUUID, 0)); // Automated island ownership transfer if (plugin.getSettings().isEnableAutoOwnershipTransfer()) { @@ -157,53 +155,53 @@ public class JoinLeaveListener implements Listener { private void runAutomatedOwnershipTransfer(User user) { plugin.getIWM().getOverWorlds().stream() - .filter(world -> plugin.getIslands().hasIsland(world, user) && !plugin.getIslands().isOwner(world, user.getUniqueId())) - .forEach(world -> { - Island island = plugin.getIslands().getIsland(world, user); + .filter(world -> plugin.getIslands().hasIsland(world, user) && !plugin.getIslands().isOwner(world, user.getUniqueId())) + .forEach(world -> { + Island island = plugin.getIslands().getIsland(world, user); - OfflinePlayer owner = Bukkit.getOfflinePlayer(island.getOwner()); + OfflinePlayer owner = Bukkit.getOfflinePlayer(island.getOwner()); - // Converting the setting (in days) to milliseconds. - long inactivityThreshold = plugin.getSettings().getAutoOwnershipTransferInactivityThreshold() * 24 * 60 * 60 * 1000L; - long timestamp = System.currentTimeMillis() - inactivityThreshold; + // Converting the setting (in days) to milliseconds. + long inactivityThreshold = plugin.getSettings().getAutoOwnershipTransferInactivityThreshold() * 24 * 60 * 60 * 1000L; + long timestamp = System.currentTimeMillis() - inactivityThreshold; - // We make sure the current owner is inactive. - if (owner.getLastPlayed() != 0 && owner.getLastPlayed() < timestamp) { - // The current owner is inactive - // Now, let's run through all of the island members (except the player who's just joined) and see who's active. - // Sadly, this will make us calculate the owner inactivity again... :( - List candidates = Arrays.asList((UUID[]) island.getMemberSet().stream() - .filter(uuid -> !user.getUniqueId().equals(uuid)) - .filter(uuid -> Bukkit.getOfflinePlayer(uuid).getLastPlayed() != 0 - && Bukkit.getOfflinePlayer(uuid).getLastPlayed() < timestamp) - .toArray()); + // We make sure the current owner is inactive. + if (owner.getLastPlayed() != 0 && owner.getLastPlayed() < timestamp) { + // The current owner is inactive + // Now, let's run through all of the island members (except the player who's just joined) and see who's active. + // Sadly, this will make us calculate the owner inactivity again... :( + List candidates = Arrays.asList((UUID[]) island.getMemberSet().stream() + .filter(uuid -> !user.getUniqueId().equals(uuid)) + .filter(uuid -> Bukkit.getOfflinePlayer(uuid).getLastPlayed() != 0 + && Bukkit.getOfflinePlayer(uuid).getLastPlayed() < timestamp) + .toArray()); - if (!candidates.isEmpty() && !plugin.getSettings().isAutoOwnershipTransferIgnoreRanks()) { - // Ranks are not ignored, our candidates can only have the highest rank - // TODO Complete this section - } - } - }); + if (!candidates.isEmpty() && !plugin.getSettings().isAutoOwnershipTransferIgnoreRanks()) { + // Ranks are not ignored, our candidates can only have the highest rank + // TODO Complete this section + } + } + }); } private void updateIslandRange(User user) { plugin.getIWM().getOverWorlds().stream() - .filter(world -> plugin.getIslands().isOwner(world, user.getUniqueId())) - .forEach(world -> { - Island island = plugin.getIslands().getIsland(world, user); - if (island != null) { - // Check if new owner has a different range permission than the island size - int range = user.getPermissionValue(plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range", island.getProtectionRange()); + .filter(world -> plugin.getIslands().isOwner(world, user.getUniqueId())) + .forEach(world -> { + Island island = plugin.getIslands().getIsland(world, user); + if (island != null) { + // Check if new owner has a different range permission than the island size + int range = user.getPermissionValue(plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range", island.getProtectionRange()); - // Range can go up or down - if (range != island.getProtectionRange()) { - user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range)); - plugin.log("Island protection range changed from " + island.getProtectionRange() + " to " - + range + " for " + user.getName() + " due to permission."); - } - island.setProtectionRange(range); - } - }); + // Range can go up or down + if (range != island.getProtectionRange()) { + user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range)); + plugin.log("Island protection range changed from " + island.getProtectionRange() + " to " + + range + " for " + user.getName() + " due to permission."); + } + island.setProtectionRange(range); + } + }); } @EventHandler(priority = EventPriority.NORMAL) @@ -218,9 +216,9 @@ public class JoinLeaveListener implements Listener { // No, there are no more players online on this island // Tell players they are being removed island.getMembers().entrySet().stream() - .filter(e -> e.getValue() == RanksManager.COOP_RANK) - .forEach(e -> User.getInstance(e.getKey()) - .sendMessage("commands.island.team.uncoop.all-members-logged-off", TextVariables.NAME, plugin.getPlayers().getName(island.getOwner()))); + .filter(e -> e.getValue() == RanksManager.COOP_RANK) + .forEach(e -> User.getInstance(e.getKey()) + .sendMessage("commands.island.team.uncoop.all-members-logged-off", TextVariables.NAME, plugin.getPlayers().getName(island.getOwner()))); // Remove any coop players on this island island.removeRank(RanksManager.COOP_RANK); } 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/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/panels/IslandCreationPanel.java b/src/main/java/world/bentobox/bentobox/panels/IslandCreationPanel.java index 9c6c30e9a..b4d5a21d5 100644 --- a/src/main/java/world/bentobox/bentobox/panels/IslandCreationPanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/IslandCreationPanel.java @@ -9,7 +9,6 @@ import org.bukkit.ChatColor; import org.eclipse.jdt.annotation.NonNull; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; 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/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()); } From f67baf6501db9055a373f1bf46b90308dae4051c Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 14 Nov 2019 17:44:56 -0800 Subject: [PATCH 2/3] Add protection for firework explosions from crossbows. https://github.com/BentoBoxWorld/BentoBox/issues/1027 --- .../flags/protection/HurtingListener.java | 62 +++++--- .../listeners/flags/settings/PVPListener.java | 48 ++++-- .../flags/protection/HurtingListenerTest.java | 3 - .../flags/settings/PVPListenerTest.java | 147 ++++++++++++++++-- 4 files changed, 209 insertions(+), 51 deletions(-) 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..1f16ff55e 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,10 +1,14 @@ 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.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; @@ -14,6 +18,7 @@ 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 @@ -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 Villager) { + 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/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/test/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/HurtingListenerTest.java index 405ed0912..e6b3e0a22 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; 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()); + } + } From 2c8739a48b08a3acc7375f5f597ce747469266f6 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 14 Nov 2019 18:05:55 -0800 Subject: [PATCH 3/3] Fixes damage to wandering traders. https://github.com/BentoBoxWorld/BentoBox/issues/1029 --- .../protection/EntityInteractListener.java | 4 +- .../flags/protection/HurtingListener.java | 10 +-- .../EntityInteractListenerTest.java | 71 +++++++++++++++++-- .../flags/protection/HurtingListenerTest.java | 35 +++++++++ .../protection/InventoryListenerTest.java | 3 +- 5 files changed, 111 insertions(+), 12 deletions(-) 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 1f16ff55e..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 @@ -5,6 +5,7 @@ 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; @@ -13,7 +14,6 @@ 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; @@ -53,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); @@ -91,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(); } @@ -145,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()); } @@ -198,7 +198,7 @@ public class HurtingListener extends FlagListener { checkIsland(e, attacker, entity.getLocation(), Flags.HURT_ANIMALS); } // Villagers being hurt - if (entity instanceof Villager) { + if (entity instanceof AbstractVillager) { checkIsland(e, attacker, entity.getLocation(), Flags.HURT_VILLAGERS); } 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 e6b3e0a22..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 @@ -27,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; @@ -330,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)}. @@ -347,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;