package com.massivecraft.factions.engine; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.UUID; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.entity.TNTPrimed; import org.bukkit.event.block.BlockFromToEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.plugin.Plugin; import org.bukkit.Location; import org.bukkit.Material; import com.massivecraft.factions.Factions; import com.massivecraft.factions.entity.MConf; import com.massivecraft.massivecore.EngineAbstract; import com.massivecraft.massivecore.ps.PS; import com.massivecraft.massivecore.util.MUtil; public class EngineExploit extends EngineAbstract { // -------------------------------------------- // // INSTANCE & CONSTRUCT // -------------------------------------------- // private static EngineExploit i = new EngineExploit(); public static EngineExploit get() { return i; } // -------------------------------------------- // // OVERRIDE // -------------------------------------------- // @Override public Plugin getPlugin() { return Factions.get(); } // -------------------------------------------- // // OBSIDIAN GENERATORS // -------------------------------------------- // @SuppressWarnings("deprecation") @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void obsidianGenerators(BlockFromToEvent event) { if (!MConf.get().handleExploitObsidianGenerators) return; // thanks to ObGenBlocker and WorldGuard for this method Block block = event.getToBlock(); int source = event.getBlock().getTypeId(); int target = block.getTypeId(); if ((target == 55 || target == 132) && (source == 0 || source == 10 || source == 11)) { block.setType(Material.AIR); } } // -------------------------------------------- // // ENDER PEARL CLIPPING // -------------------------------------------- // @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void enderPearlClipping(PlayerTeleportEvent event) { if (!MConf.get().handleExploitEnderPearlClipping) return; if (event.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) return; // this exploit works when the target location is within 0.31 blocks or so of a door or glass block or similar... Location target = event.getTo(); Location from = event.getFrom(); // blocks who occupy less than 1 block width or length wise need to be handled differently Material mat = event.getTo().getBlock().getType(); if ( ((mat == Material.THIN_GLASS || mat == Material.IRON_FENCE) && clippingThrough(target, from, 0.65)) || ((mat == Material.FENCE || mat == Material.NETHER_FENCE) && clippingThrough(target, from, 0.45)) ) { event.setTo(from); return; } // simple fix otherwise: ender pearl target locations are standardized to be in the center (X/Z) of the target block, not at the edges target.setX(target.getBlockX() + 0.5); target.setZ(target.getBlockZ() + 0.5); event.setTo(target); } public static boolean clippingThrough(Location target, Location from, double thickness) { return ( (from.getX() > target.getX() && (from.getX() - target.getX() < thickness)) || (target.getX() > from.getX() && (target.getX() - from.getX() < thickness)) || (from.getZ() > target.getZ() && (from.getZ() - target.getZ() < thickness)) || (target.getZ() > from.getZ() && (target.getZ() - from.getZ() < thickness)) ); } // -------------------------------------------- // // TNT WATERLOG // -------------------------------------------- // // TNT in water/lava doesn't normally destroy any surrounding blocks, which is usually desired behavior. // But this optional change below provides workaround for waterwalling providing perfect protection, // and makes cheap (non-obsidian) TNT cannons require minor maintenance between shots. @SuppressWarnings("deprecation") @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void tntWaterlog(EntityExplodeEvent event) { if (!MConf.get().handleExploitTNTWaterlog) return; if (!(event.getEntity() instanceof TNTPrimed)) return; Block center = event.getLocation().getBlock(); if (!center.isLiquid()) return; // a single surrounding block in all 6 directions is broken if the material is weak enough List targets = new ArrayList(); targets.add(center.getRelative(0, 0, 1)); targets.add(center.getRelative(0, 0, -1)); targets.add(center.getRelative(0, 1, 0)); targets.add(center.getRelative(0, -1, 0)); targets.add(center.getRelative(1, 0, 0)); targets.add(center.getRelative(-1, 0, 0)); for (Block target : targets) { int id = target.getTypeId(); // ignore air, bedrock, water, lava, obsidian, enchanting table, etc.... too bad we can't get a blast resistance value through Bukkit yet if (id != 0 && (id < 7 || id > 11) && id != 49 && id != 90 && id != 116 && id != 119 && id != 120 && id != 130) { target.breakNaturally(); } } } // -------------------------------------------- // // NETHER PORTAL TRAP // -------------------------------------------- // // A nether portal trap can be created by the destination portal being enclosed (trapped) - resulting in the player not being able to run commands. // This fix removes the portal blocks (client side) from the destination until they are away from the portal. private int NETHER_TRAP_RADIUS_CHECK = 5; private int NETHER_TRAP_RESET_RADIUS = 3; private HashMap> netherTrapBlockSet = new HashMap>(); // Detect teleport from a nether portal and remove animation if required @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void netherportalTrapRemoveAnimation(PlayerTeleportEvent event) { if ( ! MConf.get().handleNetherPortalTrap) return; if (event.getCause() != TeleportCause.NETHER_PORTAL) return; // If they can build at the target destination then we will not do any further checks if (EngineMain.canPlayerBuildAt(event.getPlayer(), PS.valueOf(event.getTo()), false)) return; final Player player = event.getPlayer(); final UUID uuid = player.getUniqueId(); Block from = event.getTo().getBlock(); // If a list exists, then we're dealing with a new portal - so revert the old portal blocks this.netherportalReset(player); // Store some temporary data this.netherTrapBlockSet.put(uuid, this.getPortalBlocks(from)); // Send the air update this.netherportalSendAir(player); } // When they leave the portal we will update it, otherwise send the updates again @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void netherportalTrapUpdate(PlayerMoveEvent event) { if ( ! MConf.get().handleNetherPortalTrap) return; // Only check if we're changing blocks if (MUtil.isSameBlock(event)) return; Player player = event.getPlayer(); UUID uuid = player.getUniqueId(); // Check if we're sending updates to this player if ( ! this.netherTrapBlockSet.containsKey(uuid)) return; // If they've changed worlds we don't bother checking this if (event.getTo().getWorld() != this.netherTrapBlockSet.get(uuid).get(0).getWorld()) { this.netherportalReset(player); return; } // When the player moves away from the portal, we put it back to normal if (event.getTo().distance(this.netherTrapBlockSet.get(uuid).get(0).getLocation()) > NETHER_TRAP_RESET_RADIUS) { this.netherportalReset(player); } else { // Send updates as air this.netherportalSendAir(player); } } // Sends block update to player with original blocks (if required) @SuppressWarnings("deprecation") public void netherportalReset(Player player) { UUID uuid = player.getUniqueId(); if ( ! this.netherTrapBlockSet.containsKey(uuid)) return; // Only send updates if they're in the same world if (this.netherTrapBlockSet.get(uuid).get(0).getWorld() == player.getWorld()) for (Block block : this.netherTrapBlockSet.get(uuid)) player.sendBlockChange(block.getLocation(), block.getType(), block.getData()); // Remove the block set this.netherTrapBlockSet.remove(uuid); } // Send air block updates to player @SuppressWarnings("deprecation") public void netherportalSendAir(Player player) { for (Block block : this.netherTrapBlockSet.get(player.getUniqueId())) player.sendBlockChange(block.getLocation(), Material.AIR, (byte) 0); } // Get portal blocks near a block public List getPortalBlocks(Block from) { List blocks = new ArrayList(); // Check in a radius of the block to find the portal blocks for (int x = -(NETHER_TRAP_RADIUS_CHECK); x <= NETHER_TRAP_RADIUS_CHECK; x ++) { for (int y = -(NETHER_TRAP_RADIUS_CHECK); y <= NETHER_TRAP_RADIUS_CHECK; y ++) { for (int z = -(NETHER_TRAP_RADIUS_CHECK); z <= NETHER_TRAP_RADIUS_CHECK; z ++) { if (from.getRelative(x, y, z).getType() == Material.PORTAL) { // Store block blocks.add(from.getRelative(x, y, z)); } } } } return blocks; } }