package net.knarcraft.factions.engine; import net.knarcraft.factions.entity.MConf; import com.massivecraft.massivecore.Engine; import com.massivecraft.massivecore.collections.MassiveList; import com.massivecraft.massivecore.ps.PS; import com.massivecraft.massivecore.util.MUtil; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockFromToEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import java.util.HashMap; import java.util.List; import java.util.UUID; public class EngineExploit extends Engine { // -------------------------------------------- // // INSTANCE & CONSTRUCT // -------------------------------------------- // private static EngineExploit i = new EngineExploit(); public static EngineExploit get() { return i; } // -------------------------------------------- // // OBSIDIAN GENERATORS // -------------------------------------------- // @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(); Material source = event.getBlock().getType(); Material target = block.getType(); if ((target == Material.REDSTONE_WIRE || target == Material.TRIPWIRE) && (source == Material.AIR || source == Material.LAVA)) { 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.GLASS_PANE || mat == Material.IRON_BARS) && clippingThrough(target, from, 0.65)) || ((MUtil.list(Material.ACACIA_FENCE, Material.BIRCH_FENCE, Material.DARK_OAK_FENCE, Material.JUNGLE_FENCE, Material.NETHER_BRICK_FENCE, Material.OAK_FENCE, Material.SPRUCE_FENCE).contains(mat)) && 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)) ); } // -------------------------------------------- // // 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 static final int NETHER_TRAP_RADIUS_CHECK = 5; private static final int NETHER_TRAP_RESET_RADIUS_SQUARED = 9; private HashMap> portalTraps = new HashMap<>(); @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void portalTrapRemoveAnimation(PlayerTeleportEvent event) { // If there is a teleport caused by a nether portal ... if (!MConf.get().handleNetherPortalTrap || event.getCause() != TeleportCause.NETHER_PORTAL) { return; } Player player = event.getPlayer(); Block from = event.getTo().getBlock(); // ... and the player can't build at the destination ... if (EnginePermBuild.canPlayerBuildAt(player, PS.valueOf(from), false)) { return; } // ... reset the old portal blocks stored ... this.portalReset(player); // ... get all the portal blocks belonging to the new portal at the destination ... List portalTrap = getPortal(from); if (portalTrap.isEmpty()) { return; } // ... and then store those blocks and send an update as if they were air. this.portalTraps.put(player.getUniqueId(), portalTrap); portalUpdateAir(player, portalTrap); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void portalUpdate(PlayerMoveEvent event) { // If a player moves ... if (!MConf.get().handleNetherPortalTrap || MUtil.isSameBlock(event)) { return; } Player player = event.getPlayer(); UUID uuid = player.getUniqueId(); // ... and he recently used a portal ... List portalTrap = this.portalTraps.get(uuid); if (portalTrap == null) { return; } Location locationTo = event.getTo(); Location locationFrom = portalTrap.get(0).getLocation(); World worldTo = locationTo.getWorld(); World worldFrom = locationFrom.getWorld(); // ... update reset the portal near them, if they have moved away too far ... if (!worldTo.equals(worldFrom) || locationTo.distanceSquared(locationFrom) > NETHER_TRAP_RESET_RADIUS_SQUARED) { portalUpdateReset(player, portalTrap); return; } // ... or send an update as if the portal blocks were air. portalUpdateAir(player, portalTrap); } public void portalReset(Player player) { UUID uuid = player.getUniqueId(); // If a player has already a portal registered to him ... List portalTrap = this.portalTraps.get(uuid); if (portalTrap == null) { return; } // ... remove them from the registry ... this.portalTraps.remove(uuid); // ... and send updates if the player and portal are in the same world. if (!player.getWorld().equals(portalTrap.get(0).getWorld())) { return; } portalUpdateReset(player, portalTrap); } public static void portalUpdateReset(Player player, List portal) { portalUpdate(player, portal, null, null); } public static void portalUpdateAir(Player player, List portal) { portalUpdate(player, portal, Material.AIR, (byte) 0); } @SuppressWarnings("deprecation") private static void portalUpdate(Player player, List portal, Material material, Byte data) { boolean usingDefault = material == null && data == null; for (Block block : portal) { Material updateMaterial = usingDefault ? block.getType() : material; byte updateData = usingDefault ? block.getData() : data; player.sendBlockChange(block.getLocation(), updateMaterial, updateData); } } public static List getPortal(Block from) { // Create List ret = new MassiveList<>(); // Fill - 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.NETHER_PORTAL) { ret.add(from.getRelative(x, y, z)); } } } } // Return return ret; } }