package fr.neatmonster.nocheatplus.checks.blockinteract;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckListener;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.combined.CombinedConfig;
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.BlockProperties;
/**
* Central location to listen to events that are relevant for the block interact checks.
*
* @see BlockInteractEvent
*/
public class BlockInteractListener extends CheckListener {
/** The looking-direction check. */
private final Direction direction = addCheck(new Direction());
/** The reach-distance check. */
private final Reach reach = addCheck(new Reach());
/** Interact with visible blocks. */
private final Visible visible = addCheck(new Visible());
/** Speed of interaction. */
private final Speed speed = addCheck(new Speed());
/** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */
private final Location useLoc = new Location(null, 0, 0, 0);
private final Counters counters = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class);
private final int idCancelDead = counters.registerKey("canceldead");
public BlockInteractListener(){
super(CheckType.BLOCKINTERACT);
}
/**
* We listen to PlayerInteractEvent events for obvious reasons.
*
* @param event
* the event
*/
@EventHandler(
ignoreCancelled = false, priority = EventPriority.LOWEST)
protected void onPlayerInteract(final PlayerInteractEvent event) {
final Player player = event.getPlayer();
// Cancel interact events for dead players.
if (player.isDead() && BridgeHealth.getHealth(player) <= 0.0) {
// Auto-soup after death.
event.setUseInteractedBlock(Result.DENY);
event.setUseItemInHand(Result.DENY);
event.setCancelled(true);
counters.addPrimaryThread(idCancelDead, 1);
return;
}
// TODO: Re-arrange for interact spam, possibly move ender pearl stuff to a method.
final Action action = event.getAction();
final Block block = event.getClickedBlock();
if (block == null){
return;
}
final BlockInteractData data = BlockInteractData.getData(player);
data.setLastBlock(block, action);
switch(action){
case LEFT_CLICK_BLOCK:
break;
case RIGHT_CLICK_BLOCK:
final ItemStack stack = player.getItemInHand();
if (stack != null && stack.getType() == Material.ENDER_PEARL){
if (!BlockProperties.isPassable(block.getType())){
final CombinedConfig ccc = CombinedConfig.getConfig(player);
if (ccc.enderPearlCheck && ccc.enderPearlPreventClickBlock){
event.setUseItemInHand(Result.DENY);
}
}
}
break;
default:
return;
}
if (event.isCancelled()){
return;
}
final BlockInteractConfig cc = BlockInteractConfig.getConfig(player);
boolean cancelled = false;
final BlockFace face = event.getBlockFace();
final Location loc = player.getLocation(useLoc);
// Interaction speed.
if (!cancelled && speed.isEnabled(player) && speed.check(player, data, cc)){
cancelled = true;
}
// First the reach check.
if (!cancelled && reach.isEnabled(player) && reach.check(player, loc, block, data, cc)){
cancelled = true;
}
// Second the direction check
if (!cancelled && direction.isEnabled(player) && direction.check(player, loc, block, data, cc)){
cancelled = true;
}
// Ray tracing for freecam use etc.
if (!cancelled && visible.isEnabled(player) && visible.check(player, loc, block, face, action, data, cc)){
cancelled = true;
}
// If one of the checks requested to cancel the event, do so.
if (cancelled) {
event.setUseInteractedBlock(Result.DENY);
event.setUseItemInHand(Result.DENY);
event.setCancelled(true);
}
useLoc.setWorld(null);
}
}